@twin.org/node-core 0.0.2-next.2 → 0.0.2-next.21

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 (52) hide show
  1. package/dist/cjs/index.cjs +864 -391
  2. package/dist/esm/index.mjs +852 -394
  3. package/dist/types/bootstrap.d.ts +9 -9
  4. package/dist/types/builders/engineEnvBuilder.d.ts +1 -1
  5. package/dist/types/builders/engineServerEnvBuilder.d.ts +3 -2
  6. package/dist/types/builders/extensionsBuilder.d.ts +32 -0
  7. package/dist/types/defaults.d.ts +6 -0
  8. package/dist/types/index.d.ts +4 -0
  9. package/dist/types/models/IEngineEnvironmentVariables.d.ts +188 -24
  10. package/dist/types/models/IEngineServerEnvironmentVariables.d.ts +12 -3
  11. package/dist/types/models/INodeEngineConfig.d.ts +6 -0
  12. package/dist/types/models/INodeEnvironmentVariables.d.ts +1 -2
  13. package/dist/types/models/INodeOptions.d.ts +6 -1
  14. package/dist/types/models/nodeExtensionMethods.d.ts +27 -0
  15. package/dist/types/models/nodeFeatures.d.ts +4 -0
  16. package/dist/types/node.d.ts +7 -2
  17. package/dist/types/server.d.ts +4 -2
  18. package/dist/types/utils.d.ts +24 -0
  19. package/docs/changelog.md +138 -0
  20. package/docs/reference/functions/{bootstrapAttestationMethod.md → bootstrapSynchronisedStorage.md} +3 -3
  21. package/docs/reference/functions/buildConfiguration.md +2 -2
  22. package/docs/reference/functions/buildEngineConfiguration.md +2 -2
  23. package/docs/reference/functions/buildEngineServerConfiguration.md +9 -3
  24. package/docs/reference/functions/directoryExists.md +19 -0
  25. package/docs/reference/functions/extensionsConfiguration.md +25 -0
  26. package/docs/reference/functions/extensionsInitialiseEngine.md +25 -0
  27. package/docs/reference/functions/extensionsInitialiseEngineServer.md +31 -0
  28. package/docs/reference/functions/getFiles.md +19 -0
  29. package/docs/reference/functions/getSubFolders.md +19 -0
  30. package/docs/reference/functions/loadTextFile.md +19 -0
  31. package/docs/reference/functions/overrideModuleImport.md +17 -0
  32. package/docs/reference/functions/shutdownExtensions.md +25 -0
  33. package/docs/reference/functions/start.md +4 -4
  34. package/docs/reference/index.md +21 -1
  35. package/docs/reference/interfaces/IEngineEnvironmentVariables.md +374 -36
  36. package/docs/reference/interfaces/IEngineServerEnvironmentVariables.md +1673 -5
  37. package/docs/reference/interfaces/INodeEngineConfig.md +7 -0
  38. package/docs/reference/interfaces/INodeEnvironmentVariables.md +643 -137
  39. package/docs/reference/interfaces/INodeOptions.md +14 -2
  40. package/docs/reference/type-aliases/NodeExtensionInitialiseEngineMethod.md +18 -0
  41. package/docs/reference/type-aliases/NodeExtensionInitialiseEngineServerMethod.md +24 -0
  42. package/docs/reference/type-aliases/NodeExtensionInitialiseMethod.md +23 -0
  43. package/docs/reference/type-aliases/NodeExtensionShutdownMethod.md +10 -0
  44. package/docs/reference/variables/ATTESTATION_VERIFICATION_METHOD_ID.md +3 -0
  45. package/docs/reference/variables/AUTH_SIGNING_KEY_ID.md +3 -0
  46. package/docs/reference/variables/BLOB_STORAGE_ENCRYPTION_KEY_ID.md +3 -0
  47. package/docs/reference/variables/IMMUTABLE_PROOF_VERIFICATION_METHOD_ID.md +3 -0
  48. package/docs/reference/variables/NodeFeatures.md +7 -1
  49. package/docs/reference/variables/SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID.md +3 -0
  50. package/docs/reference/variables/VC_AUTHENTICATION_VERIFICATION_METHOD_ID.md +3 -0
  51. package/locales/en.json +15 -7
  52. package/package.json +18 -2
@@ -3,6 +3,7 @@
3
3
  var apiAuthEntityStorageService = require('@twin.org/api-auth-entity-storage-service');
4
4
  var core = require('@twin.org/core');
5
5
  var crypto = require('@twin.org/crypto');
6
+ var engineServerTypes = require('@twin.org/engine-server-types');
6
7
  var engineTypes = require('@twin.org/engine-types');
7
8
  var entityStorageModels = require('@twin.org/entity-storage-models');
8
9
  var identityModels = require('@twin.org/identity-models');
@@ -10,8 +11,10 @@ var vaultModels = require('@twin.org/vault-models');
10
11
  var walletModels = require('@twin.org/wallet-models');
11
12
  var promises = require('node:fs/promises');
12
13
  var path = require('node:path');
14
+ var cliCore = require('@twin.org/cli-core');
15
+ var rightsManagementRestClient = require('@twin.org/rights-management-rest-client');
13
16
  var engineServer = require('@twin.org/engine-server');
14
- var engineServerTypes = require('@twin.org/engine-server-types');
17
+ var modules = require('@twin.org/modules');
15
18
  var dotenv = require('dotenv');
16
19
  var engine = require('@twin.org/engine');
17
20
  var engineCore = require('@twin.org/engine-core');
@@ -36,6 +39,15 @@ function _interopNamespaceDefault(e) {
36
39
 
37
40
  var dotenv__namespace = /*#__PURE__*/_interopNamespaceDefault(dotenv);
38
41
 
42
+ // Copyright 2024 IOTA Stiftung.
43
+ // SPDX-License-Identifier: Apache-2.0.
44
+ const ATTESTATION_VERIFICATION_METHOD_ID = "attestation-assertion";
45
+ const IMMUTABLE_PROOF_VERIFICATION_METHOD_ID = "immutable-proof-assertion";
46
+ const BLOB_STORAGE_ENCRYPTION_KEY_ID = "blob-encryption";
47
+ const SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID = "synchronised-storage-blob-encryption";
48
+ const VC_AUTHENTICATION_VERIFICATION_METHOD_ID = "node-authentication-assertion";
49
+ const AUTH_SIGNING_KEY_ID = "auth-signing";
50
+
39
51
  // Copyright 2024 IOTA Stiftung.
40
52
  // SPDX-License-Identifier: Apache-2.0.
41
53
  /**
@@ -50,25 +62,28 @@ const NodeFeatures = {
50
62
  /**
51
63
  * NodeUser - generates a user for the node if not provided in config.
52
64
  */
53
- NodeUser: "node-user"
65
+ NodeUser: "node-user",
66
+ /**
67
+ * NodeWallet - generates a wallet for the node and funds it when there is a faucet available.
68
+ */
69
+ NodeWallet: "node-wallet"
54
70
  };
55
71
 
56
72
  // Copyright 2024 IOTA Stiftung.
57
73
  // SPDX-License-Identifier: Apache-2.0.
58
- /* eslint-disable no-console */
59
74
  /**
60
75
  * Initialise the locales for the application.
61
76
  * @param localesDirectory The directory containing the locales.
62
77
  */
63
78
  async function initialiseLocales(localesDirectory) {
64
79
  const localesFile = path.resolve(path.join(localesDirectory, "en.json"));
65
- console.info("Locales File:", localesFile);
80
+ cliCore.CLIDisplay.value("Locales File", localesFile);
66
81
  if (await fileExists(localesFile)) {
67
82
  const enLangContent = await promises.readFile(localesFile, "utf8");
68
83
  core.I18n.addDictionary("en", JSON.parse(enLangContent));
69
84
  }
70
85
  else {
71
- console.warn(`Locales file not found: ${localesFile}`);
86
+ cliCore.CLIDisplay.error(`Locales file not found: ${localesFile}`);
72
87
  }
73
88
  }
74
89
  /**
@@ -92,13 +107,79 @@ async function fileExists(filename) {
92
107
  return false;
93
108
  }
94
109
  }
110
+ /**
111
+ * Does the specified directory exist.
112
+ * @param directory The directory to check for existence.
113
+ * @returns True if the directory exists.
114
+ */
115
+ async function directoryExists(directory) {
116
+ try {
117
+ const stats = await promises.stat(directory);
118
+ return stats.isDirectory();
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ }
124
+ /**
125
+ * Get the sub folders for the folder.
126
+ * @param directory The directory to get the sub folders.
127
+ * @returns The list of sub folders.
128
+ */
129
+ async function getSubFolders(directory) {
130
+ try {
131
+ const dir = await promises.readdir(directory);
132
+ const folders = [];
133
+ for (const dirEntry of dir) {
134
+ const fullPath = path.join(directory, dirEntry);
135
+ const stats = await promises.stat(fullPath);
136
+ if (stats.isDirectory()) {
137
+ folders.push(fullPath);
138
+ }
139
+ }
140
+ return folders;
141
+ }
142
+ catch {
143
+ return [];
144
+ }
145
+ }
146
+ /**
147
+ * Get the files in the directory.
148
+ * @param directory The directory to get the files from.
149
+ * @returns The list of files in the directory.
150
+ */
151
+ async function getFiles(directory) {
152
+ try {
153
+ const dir = await promises.readdir(directory);
154
+ const files = [];
155
+ for (const dirEntry of dir) {
156
+ const fullPath = path.join(directory, dirEntry);
157
+ const stats = await promises.stat(fullPath);
158
+ if (stats.isFile()) {
159
+ files.push(fullPath);
160
+ }
161
+ }
162
+ return files;
163
+ }
164
+ catch {
165
+ return [];
166
+ }
167
+ }
168
+ /**
169
+ * Load the text file.
170
+ * @param filename The filename of the text file to load.
171
+ * @returns The contents of the text file if it could not be loaded.
172
+ */
173
+ async function loadTextFile(filename) {
174
+ return promises.readFile(filename, "utf8");
175
+ }
95
176
  /**
96
177
  * Load the JSON file.
97
178
  * @param filename The filename of the JSON file to load.
98
179
  * @returns The contents of the JSON file or null if it could not be loaded.
99
180
  */
100
181
  async function loadJsonFile(filename) {
101
- const content = await promises.readFile(filename, "utf8");
182
+ const content = await loadTextFile(filename);
102
183
  return JSON.parse(content);
103
184
  }
104
185
  /**
@@ -137,8 +218,18 @@ async function bootstrap(engineCore, context, envVars) {
137
218
  await bootstrapNodeUser(engineCore, context, envVars, features);
138
219
  await bootstrapAuth(engineCore, context, envVars);
139
220
  await bootstrapBlobEncryption(engineCore, context, envVars);
140
- await bootstrapAttestationMethod(engineCore, context, envVars);
141
- await bootstrapImmutableProofMethod(engineCore, context, envVars);
221
+ const defaultAttestationConnectorType = engineCore.getRegisteredInstanceTypeOptional("attestationConnector");
222
+ if (!core.Is.empty(defaultAttestationConnectorType)) {
223
+ await addVerificationMethod(engineCore, context, "attestation", envVars.attestationVerificationMethodId ?? ATTESTATION_VERIFICATION_METHOD_ID);
224
+ }
225
+ const defaultImmutableProofComponentType = engineCore.getRegisteredInstanceTypeOptional("immutableProofComponent");
226
+ if (!core.Is.empty(defaultImmutableProofComponentType)) {
227
+ await addVerificationMethod(engineCore, context, "immutable proof", envVars.immutableProofVerificationMethodId ?? IMMUTABLE_PROOF_VERIFICATION_METHOD_ID);
228
+ }
229
+ if (core.Coerce.boolean(envVars.vcAuthenticationEnabled) ?? false) {
230
+ await addVerificationMethod(engineCore, context, "verifiable credential authentication", envVars.vcAuthenticationVerificationMethodId ?? VC_AUTHENTICATION_VERIFICATION_METHOD_ID);
231
+ }
232
+ await bootstrapSynchronisedStorage(engineCore, context, envVars);
142
233
  }
143
234
  /**
144
235
  * Bootstrap the node creating any necessary resources.
@@ -153,23 +244,21 @@ async function bootstrapNodeIdentity(engineCore, context, envVars, features) {
153
244
  // But we have a chicken and egg problem in that we can't create the identity
154
245
  // to store the mnemonic in the vault without an identity. We use a temporary identity
155
246
  // and then replace it with the new identity later in the process.
156
- const engineDefaultTypes = engineCore.getDefaultTypes();
157
- if (!core.Is.empty(engineDefaultTypes.vaultConnector)) {
158
- const vaultConnector = vaultModels.VaultConnectorFactory.get(engineDefaultTypes.vaultConnector);
159
- const workingIdentity = envVars.identity ??
160
- context.state.nodeIdentity ??
161
- `bootstrap-temp-${core.Converter.bytesToHex(core.RandomHelper.generate(16))}`;
162
- await bootstrapMnemonic(engineCore, envVars, features, vaultConnector, workingIdentity);
163
- const addresses = await bootstrapWallet(engineCore, envVars, features, workingIdentity);
164
- const finalIdentity = await bootstrapIdentity(engineCore, envVars, features, workingIdentity);
165
- await finaliseWallet(engineCore, envVars, features, finalIdentity, addresses);
166
- await finaliseMnemonic(vaultConnector, workingIdentity, finalIdentity);
167
- context.state.nodeIdentity = finalIdentity;
168
- context.stateDirty = true;
169
- engineCore.logInfo(core.I18n.formatMessage("node.nodeIdentity", {
170
- identity: context.state.nodeIdentity
171
- }));
172
- }
247
+ const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
248
+ const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
249
+ const workingIdentity = envVars.identity ??
250
+ context.state.nodeIdentity ??
251
+ `bootstrap-temp-${core.Converter.bytesToHex(core.RandomHelper.generate(16))}`;
252
+ await bootstrapMnemonic(engineCore, envVars, features, vaultConnector, workingIdentity);
253
+ const addresses = await bootstrapWallet(engineCore, envVars, features, workingIdentity);
254
+ const finalIdentity = await bootstrapIdentity(engineCore, envVars, features, workingIdentity);
255
+ await finaliseWallet(engineCore, envVars, features, finalIdentity, addresses);
256
+ await finaliseMnemonic(vaultConnector, workingIdentity, finalIdentity);
257
+ context.state.nodeIdentity = finalIdentity;
258
+ context.stateDirty = true;
259
+ engineCore.logInfo(core.I18n.formatMessage("node.nodeIdentity", {
260
+ identity: context.state.nodeIdentity
261
+ }));
173
262
  }
174
263
  }
175
264
  /**
@@ -181,12 +270,13 @@ async function bootstrapNodeIdentity(engineCore, context, envVars, features) {
181
270
  * @returns The addresses for the wallet.
182
271
  */
183
272
  async function bootstrapIdentity(engineCore, envVars, features, nodeIdentity) {
184
- const engineDefaultTypes = engineCore.getDefaultTypes();
273
+ const defaultIdentityConnectorType = engineCore.getRegisteredInstanceType("identityConnector");
185
274
  // Now create an identity for the node controlled by the address we just funded
186
- const identityConnector = identityModels.IdentityConnectorFactory.get(engineDefaultTypes.identityConnector);
275
+ const identityConnector = identityModels.IdentityConnectorFactory.get(defaultIdentityConnectorType);
187
276
  let identityDocument;
188
277
  try {
189
- const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(engineDefaultTypes.identityResolverConnector);
278
+ const defaultIdentityResolverConnectorType = engineCore.getRegisteredInstanceType("identityResolverConnector");
279
+ const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(defaultIdentityResolverConnectorType);
190
280
  identityDocument = await identityResolverConnector.resolveDocument(nodeIdentity);
191
281
  engineCore.logInfo(core.I18n.formatMessage("node.existingNodeIdentity", { identity: nodeIdentity }));
192
282
  }
@@ -196,7 +286,7 @@ async function bootstrapIdentity(engineCore, envVars, features, nodeIdentity) {
196
286
  identityDocument = await identityConnector.createDocument(nodeIdentity);
197
287
  engineCore.logInfo(core.I18n.formatMessage("node.createdNodeIdentity", { identity: identityDocument.id }));
198
288
  }
199
- if (engineDefaultTypes.identityConnector === engineTypes.IdentityConnectorType.Iota) {
289
+ if (defaultIdentityConnectorType.startsWith(engineTypes.IdentityConnectorType.Iota)) {
200
290
  const didUrn = core.Urn.fromValidString(identityDocument.id);
201
291
  const didParts = didUrn.parts();
202
292
  const objectId = didParts[3];
@@ -215,23 +305,26 @@ async function bootstrapIdentity(engineCore, envVars, features, nodeIdentity) {
215
305
  * @returns The addresses for the wallet.
216
306
  */
217
307
  async function bootstrapWallet(engineCore, envVars, features, nodeIdentity) {
218
- const engineDefaultTypes = engineCore.getDefaultTypes();
219
- const walletConnector = walletModels.WalletConnectorFactory.get(engineDefaultTypes.walletConnector);
220
- const addresses = await walletConnector.getAddresses(nodeIdentity, 0, 0, 5);
221
- const balance = await walletConnector.getBalance(nodeIdentity, addresses[0]);
222
- if (balance === 0n) {
223
- let address0 = addresses[0];
224
- if (engineDefaultTypes.walletConnector === engineTypes.WalletConnectorType.Iota) {
225
- address0 = `${envVars.iotaExplorerEndpoint}address/${address0}?network=${envVars.iotaNetwork}`;
308
+ if (features.includes(NodeFeatures.NodeWallet)) {
309
+ const defaultWalletConnectorType = engineCore.getRegisteredInstanceType("walletConnector");
310
+ const walletConnector = walletModels.WalletConnectorFactory.get(defaultWalletConnectorType);
311
+ const addresses = await walletConnector.getAddresses(nodeIdentity, 0, 0, 5);
312
+ const balance = await walletConnector.getBalance(nodeIdentity, addresses[0]);
313
+ if (balance === 0n) {
314
+ let address0 = addresses[0];
315
+ if (defaultWalletConnectorType.startsWith(engineTypes.WalletConnectorType.Iota)) {
316
+ address0 = `${envVars.iotaExplorerEndpoint}address/${address0}?network=${envVars.iotaNetwork}`;
317
+ }
318
+ engineCore.logInfo(core.I18n.formatMessage("node.fundingWallet", { address: address0 }));
319
+ // Add some funds to the wallet from the faucet
320
+ await walletConnector.ensureBalance(nodeIdentity, addresses[0], 1000000000n);
226
321
  }
227
- engineCore.logInfo(core.I18n.formatMessage("node.fundingWallet", { address: address0 }));
228
- // Add some funds to the wallet from the faucet
229
- await walletConnector.ensureBalance(nodeIdentity, addresses[0], 1000000000n);
230
- }
231
- else {
232
- engineCore.logInfo(core.I18n.formatMessage("node.fundedWallet"));
322
+ else {
323
+ engineCore.logInfo(core.I18n.formatMessage("node.fundedWallet"));
324
+ }
325
+ return addresses;
233
326
  }
234
- return addresses;
327
+ return [];
235
328
  }
236
329
  /**
237
330
  * Bootstrap the identity for the node.
@@ -242,15 +335,17 @@ async function bootstrapWallet(engineCore, envVars, features, nodeIdentity) {
242
335
  * @param addresses The addresses for the wallet.
243
336
  */
244
337
  async function finaliseWallet(engineCore, envVars, features, finalIdentity, addresses) {
245
- const engineDefaultTypes = engineCore.getDefaultTypes();
246
- // If we are using entity storage for wallet the identity associated with the
247
- // address will be wrong, so fix it
248
- if (engineDefaultTypes.walletConnector === "entity-storage") {
249
- const walletAddress = entityStorageModels.EntityStorageConnectorFactory.get(core.StringHelper.kebabCase("WalletAddress"));
250
- const addr = await walletAddress.get(addresses[0]);
251
- if (!core.Is.empty(addr)) {
252
- addr.identity = finalIdentity;
253
- await walletAddress.set(addr);
338
+ if (features.includes(NodeFeatures.NodeWallet)) {
339
+ const defaultWalletConnectorType = engineCore.getRegisteredInstanceType("walletConnector");
340
+ // If we are using entity storage for wallet the identity associated with the
341
+ // address will be wrong, so fix it
342
+ if (defaultWalletConnectorType.startsWith(engineTypes.WalletConnectorType.EntityStorage)) {
343
+ const walletAddress = entityStorageModels.EntityStorageConnectorFactory.get("wallet-address");
344
+ const addr = await walletAddress.get(addresses[0]);
345
+ if (!core.Is.empty(addr)) {
346
+ addr.identity = finalIdentity;
347
+ await walletAddress.set(addr);
348
+ }
254
349
  }
255
350
  }
256
351
  }
@@ -312,10 +407,10 @@ async function finaliseMnemonic(vaultConnector, workingIdentity, finalIdentity)
312
407
  */
313
408
  async function bootstrapNodeUser(engineCore, context, envVars, features) {
314
409
  if (features.includes(NodeFeatures.NodeUser)) {
315
- const engineDefaultTypes = engineCore.getDefaultTypes();
316
- if (engineDefaultTypes.authenticationComponent === "authentication-entity-storage" &&
410
+ const defaultAuthenticationComponentType = engineCore.getRegisteredInstanceType("authenticationComponent");
411
+ if (defaultAuthenticationComponentType.startsWith(engineServerTypes.AuthenticationComponentType.EntityStorage) &&
317
412
  core.Is.stringValue(context.state.nodeIdentity)) {
318
- const authUserEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(core.StringHelper.kebabCase("AuthenticationUser"));
413
+ const authUserEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get("authentication-user");
319
414
  const email = envVars.username ?? DEFAULT_NODE_USERNAME;
320
415
  let nodeAdminUser = await authUserEntityStorage.get(email);
321
416
  if (core.Is.empty(nodeAdminUser)) {
@@ -356,7 +451,8 @@ async function bootstrapNodeUser(engineCore, context, envVars, features) {
356
451
  }
357
452
  }
358
453
  // We have create a node user, now we need to create a profile for the user
359
- const identityProfileConnector = identityModels.IdentityProfileConnectorFactory.get(engineDefaultTypes.identityProfileConnector);
454
+ const defaultIdentityProfileConnectorType = engineCore.getRegisteredInstanceType("identityProfileConnector");
455
+ const identityProfileConnector = identityModels.IdentityProfileConnectorFactory.get(defaultIdentityProfileConnectorType);
360
456
  if (identityProfileConnector) {
361
457
  let userProfile;
362
458
  try {
@@ -386,43 +482,6 @@ async function bootstrapNodeUser(engineCore, context, envVars, features) {
386
482
  }
387
483
  }
388
484
  }
389
- /**
390
- * Bootstrap the attestation verification methods.
391
- * @param engineCore The engine core for the node.
392
- * @param context The context for the node.
393
- * @param envVars The environment variables for the node.
394
- * @param features The features that are enabled on the node.
395
- */
396
- async function bootstrapAttestationMethod(engineCore, context, envVars, features) {
397
- if (core.Is.stringValue(context.state.nodeIdentity) &&
398
- core.Is.arrayValue(context.config.types.identityConnector) &&
399
- core.Is.stringValue(envVars.attestationVerificationMethodId)) {
400
- const engineDefaultTypes = engineCore.getDefaultTypes();
401
- const identityConnector = identityModels.IdentityConnectorFactory.get(engineDefaultTypes.identityConnector);
402
- const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(engineDefaultTypes.identityResolverConnector);
403
- const identityDocument = await identityResolverConnector.resolveDocument(context.state.nodeIdentity);
404
- const fullMethodId = `${identityDocument.id}#${envVars.attestationVerificationMethodId}`;
405
- let createVm = true;
406
- try {
407
- identityModels.DocumentHelper.getVerificationMethod(identityDocument, fullMethodId, "assertionMethod");
408
- createVm = false;
409
- }
410
- catch { }
411
- if (createVm) {
412
- // Add attestation verification method to DID, the correct node context is now in place
413
- // so the keys for the verification method will be stored correctly
414
- engineCore.logInfo(core.I18n.formatMessage("node.addingAttestation", {
415
- methodId: fullMethodId
416
- }));
417
- await identityConnector.addVerificationMethod(context.state.nodeIdentity, context.state.nodeIdentity, "assertionMethod", envVars.attestationVerificationMethodId);
418
- }
419
- else {
420
- engineCore.logInfo(core.I18n.formatMessage("node.existingAttestation", {
421
- methodId: fullMethodId
422
- }));
423
- }
424
- }
425
- }
426
485
  /**
427
486
  * Bootstrap the immutable proof verification methods.
428
487
  * @param engineCore The engine core for the node.
@@ -430,36 +489,7 @@ async function bootstrapAttestationMethod(engineCore, context, envVars, features
430
489
  * @param envVars The environment variables for the node.
431
490
  * @param features The features that are enabled on the node.
432
491
  */
433
- async function bootstrapImmutableProofMethod(engineCore, context, envVars, features) {
434
- const engineDefaultTypes = engineCore.getDefaultTypes();
435
- if (core.Is.stringValue(context.state.nodeIdentity) &&
436
- core.Is.arrayValue(context.config.types.identityConnector) &&
437
- core.Is.stringValue(envVars.immutableProofVerificationMethodId)) {
438
- const identityConnector = identityModels.IdentityConnectorFactory.get(engineDefaultTypes.identityConnector);
439
- const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(engineDefaultTypes.identityResolverConnector);
440
- const identityDocument = await identityResolverConnector.resolveDocument(context.state.nodeIdentity);
441
- const fullMethodId = `${identityDocument.id}#${envVars.immutableProofVerificationMethodId}`;
442
- let createVm = true;
443
- try {
444
- identityModels.DocumentHelper.getVerificationMethod(identityDocument, fullMethodId, "assertionMethod");
445
- createVm = false;
446
- }
447
- catch { }
448
- if (createVm) {
449
- // Add AIG verification method to DID, the correct node context is now in place
450
- // so the keys for the verification method will be stored correctly
451
- engineCore.logInfo(core.I18n.formatMessage("node.addingImmutableProof", {
452
- methodId: fullMethodId
453
- }));
454
- await identityConnector.addVerificationMethod(context.state.nodeIdentity, context.state.nodeIdentity, "assertionMethod", envVars.immutableProofVerificationMethodId);
455
- }
456
- else {
457
- engineCore.logInfo(core.I18n.formatMessage("node.existingImmutableProof", {
458
- methodId: fullMethodId
459
- }));
460
- }
461
- }
462
- }
492
+ async function bootstrapImmutableProofMethod(engineCore, context, envVars, features) { }
463
493
  /**
464
494
  * Bootstrap the keys for blob encryption.
465
495
  * @param engineCore The engine core for the node.
@@ -470,18 +500,28 @@ async function bootstrapImmutableProofMethod(engineCore, context, envVars, featu
470
500
  async function bootstrapBlobEncryption(engineCore, context, envVars, features) {
471
501
  if ((core.Coerce.boolean(envVars.blobStorageEnableEncryption) ?? false) &&
472
502
  core.Is.stringValue(context.state.nodeIdentity)) {
473
- const engineDefaultTypes = engineCore.getDefaultTypes();
474
503
  // Create a new key for encrypting blobs
475
- const vaultConnector = vaultModels.VaultConnectorFactory.get(engineDefaultTypes.vaultConnector);
476
- const keyName = `${context.state.nodeIdentity}/${envVars.blobStorageEncryptionKey}`;
504
+ const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
505
+ const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
506
+ const keyName = `${context.state.nodeIdentity}/${envVars.blobStorageEncryptionKeyId ?? BLOB_STORAGE_ENCRYPTION_KEY_ID}`;
477
507
  let existingKey;
478
508
  try {
479
509
  existingKey = await vaultConnector.getKey(keyName);
480
510
  }
481
511
  catch { }
482
512
  if (core.Is.empty(existingKey)) {
483
- engineCore.logInfo(core.I18n.formatMessage("node.creatingBlobEncryptionKey", { keyName }));
484
- await vaultConnector.createKey(keyName, vaultModels.VaultKeyType.ChaCha20Poly1305);
513
+ if (core.Is.stringBase64(envVars.blobStorageSymmetricEncryptionKey)) {
514
+ engineCore.logInfo(core.I18n.formatMessage("node.addingBlobEncryptionKey", { keyName }));
515
+ await vaultConnector.addKey(keyName, vaultModels.VaultKeyType.ChaCha20Poly1305, core.Converter.base64ToBytes(envVars.blobStorageSymmetricEncryptionKey));
516
+ }
517
+ else {
518
+ engineCore.logInfo(core.I18n.formatMessage("node.creatingBlobEncryptionKey", { keyName }));
519
+ const key = await vaultConnector.createKey(keyName, vaultModels.VaultKeyType.ChaCha20Poly1305);
520
+ engineCore.logInfo(core.I18n.formatMessage("node.createdBlobEncryptionKey", {
521
+ keyName,
522
+ keyValue: core.Converter.bytesToBase64(key)
523
+ }));
524
+ }
485
525
  }
486
526
  else {
487
527
  engineCore.logInfo(core.I18n.formatMessage("node.existingBlobEncryptionKey", { keyName }));
@@ -496,12 +536,14 @@ async function bootstrapBlobEncryption(engineCore, context, envVars, features) {
496
536
  * @param features The features that are enabled on the node.
497
537
  */
498
538
  async function bootstrapAuth(engineCore, context, envVars, features) {
499
- const engineDefaultTypes = engineCore.getDefaultTypes();
500
- if (engineDefaultTypes.authenticationComponent === "authentication-entity-storage" &&
539
+ const defaultAuthenticationComponentType = engineCore.getRegisteredInstanceTypeOptional("authenticationComponent");
540
+ if (core.Is.stringValue(defaultAuthenticationComponentType) &&
541
+ defaultAuthenticationComponentType.startsWith(engineServerTypes.AuthenticationComponentType.EntityStorage) &&
501
542
  core.Is.stringValue(context.state.nodeIdentity)) {
502
543
  // Create a new JWT signing key and a user login for the node
503
- const vaultConnector = vaultModels.VaultConnectorFactory.get(engineDefaultTypes.vaultConnector);
504
- const keyName = `${context.state.nodeIdentity}/${envVars.authSigningKeyId}`;
544
+ const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
545
+ const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
546
+ const keyName = `${context.state.nodeIdentity}/${envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID}`;
505
547
  let existingKey;
506
548
  try {
507
549
  existingKey = await vaultConnector.getKey(keyName);
@@ -516,6 +558,74 @@ async function bootstrapAuth(engineCore, context, envVars, features) {
516
558
  }
517
559
  }
518
560
  }
561
+ /**
562
+ * Bootstrap the synchronised storage blob encryption and verification methods.
563
+ * @param engineCore The engine core for the node.
564
+ * @param context The context for the node.
565
+ * @param envVars The environment variables for the node.
566
+ * @param features The features that are enabled on the node.
567
+ */
568
+ async function bootstrapSynchronisedStorage(engineCore, context, envVars, features) {
569
+ if (core.Coerce.boolean(envVars.synchronisedStorageEnabled) ?? false) {
570
+ // If this is a trusted node we need to add the blob encryption key pair
571
+ if (core.Is.stringBase64(envVars.synchronisedStorageBlobStorageKey)) {
572
+ const defaultVaultConnectorType = engineCore.getRegisteredInstanceType("vaultConnector");
573
+ const vaultConnector = vaultModels.VaultConnectorFactory.get(defaultVaultConnectorType);
574
+ const keyName = envVars.synchronisedStorageBlobStorageEncryptionKeyId ??
575
+ SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID;
576
+ let existingKey;
577
+ try {
578
+ existingKey = await vaultConnector.getKey(keyName);
579
+ }
580
+ catch { }
581
+ if (core.Is.empty(existingKey)) {
582
+ engineCore.logInfo(core.I18n.formatMessage("node.addingSynchronisedStorageBlobEncryptionKey", { keyName }));
583
+ await vaultConnector.addKey(keyName, vaultModels.VaultKeyType.ChaCha20Poly1305, core.Converter.base64ToBytes(envVars.synchronisedStorageBlobStorageKey));
584
+ }
585
+ else {
586
+ engineCore.logInfo(core.I18n.formatMessage("node.existingSynchronisedStorageBlobEncryptionKey", { keyName }));
587
+ }
588
+ }
589
+ }
590
+ }
591
+ /**
592
+ * Add a verification method if it doesn't exist.
593
+ * @param engineCore The engine core for the node.
594
+ * @param context The context for the node.
595
+ * @param verificationMethodTitle The verification method title.
596
+ * @param verificationMethodId The verification method ID.
597
+ */
598
+ async function addVerificationMethod(engineCore, context, verificationMethodTitle, verificationMethodId) {
599
+ if (core.Is.stringValue(context.state.nodeIdentity) &&
600
+ core.Is.arrayValue(context.config.types.identityConnector) &&
601
+ core.Is.stringValue(verificationMethodId)) {
602
+ const defaultIdentityConnectorType = engineCore.getRegisteredInstanceType("identityConnector");
603
+ const identityConnector = identityModels.IdentityConnectorFactory.get(defaultIdentityConnectorType);
604
+ const defaultIdentityResolverConnectorType = engineCore.getRegisteredInstanceType("identityResolverConnector");
605
+ const identityResolverConnector = identityModels.IdentityResolverConnectorFactory.get(defaultIdentityResolverConnectorType);
606
+ const identityDocument = await identityResolverConnector.resolveDocument(context.state.nodeIdentity);
607
+ const fullMethodId = `${identityDocument.id}#${verificationMethodId}`;
608
+ let exists = false;
609
+ try {
610
+ identityModels.DocumentHelper.getVerificationMethod(identityDocument, fullMethodId, "assertionMethod");
611
+ exists = true;
612
+ }
613
+ catch { }
614
+ if (!exists) {
615
+ engineCore.logInfo(core.I18n.formatMessage("node.addingVerificationMethod", {
616
+ title: verificationMethodTitle,
617
+ methodId: fullMethodId
618
+ }));
619
+ await identityConnector.addVerificationMethod(context.state.nodeIdentity, context.state.nodeIdentity, "assertionMethod", verificationMethodId);
620
+ }
621
+ else {
622
+ engineCore.logInfo(core.I18n.formatMessage("node.existingVerificationMethod", {
623
+ title: verificationMethodTitle,
624
+ methodId: fullMethodId
625
+ }));
626
+ }
627
+ }
628
+ }
519
629
 
520
630
  // Copyright 2024 IOTA Stiftung.
521
631
  // SPDX-License-Identifier: Apache-2.0.
@@ -524,61 +634,51 @@ async function bootstrapAuth(engineCore, context, envVars, features) {
524
634
  * @param envVars The environment variables.
525
635
  * @returns The config for the core.
526
636
  */
527
- function buildEngineConfiguration(envVars) {
637
+ async function buildEngineConfiguration(envVars) {
528
638
  if (core.Is.stringValue(envVars.storageFileRoot)) {
529
639
  envVars.stateFilename ??= "engine-state.json";
530
640
  envVars.storageFileRoot = path.resolve(envVars.storageFileRoot);
531
641
  envVars.stateFilename = path.join(envVars.storageFileRoot, envVars.stateFilename);
532
642
  }
533
- envVars.attestationVerificationMethodId ??= "attestation-assertion";
534
- envVars.immutableProofVerificationMethodId ??= "immutable-proof-assertion";
535
- envVars.blobStorageEnableEncryption ??= "false";
536
- envVars.blobStorageEncryptionKey ??= "blob-encryption";
537
643
  const coreConfig = {
538
644
  debug: core.Coerce.boolean(envVars.debug) ?? false,
539
645
  types: {}
540
646
  };
541
- configureEntityStorage(coreConfig, envVars);
542
- configureBlobStorage(coreConfig, envVars);
543
- configureVault(coreConfig, envVars);
544
- configureDlt(coreConfig, envVars);
545
- configureLogging(coreConfig, envVars);
546
- configureBackgroundTask(coreConfig, envVars);
547
- configureEventBus(coreConfig, envVars);
548
- configureTelemetry(coreConfig, envVars);
549
- configureMessaging(coreConfig, envVars);
550
- configureFaucet(coreConfig, envVars);
551
- configureWallet(coreConfig, envVars);
552
- configureNft(coreConfig, envVars);
553
- configureVerifiableStorage(coreConfig, envVars);
554
- configureIdentity(coreConfig, envVars);
555
- configureIdentityResolver(coreConfig, envVars);
556
- configureIdentityProfile(coreConfig, envVars);
557
- configureAttestation(coreConfig, envVars);
558
- configureDataProcessing(coreConfig, envVars);
559
- configureAuditableItemGraph(coreConfig);
560
- configureAuditableItemStream(coreConfig);
561
- configureDocumentManagement(coreConfig);
562
- configureFederatedCatalogue(coreConfig, envVars);
563
- configureRightsManagement(coreConfig, envVars);
564
- configureTaskScheduler(coreConfig, envVars);
647
+ await configureEntityStorage(coreConfig, envVars);
648
+ await configureBlobStorage(coreConfig, envVars);
649
+ await configureVault(coreConfig, envVars);
650
+ await configureDlt(coreConfig, envVars);
651
+ await configureLogging(coreConfig, envVars);
652
+ await configureBackgroundTask(coreConfig, envVars);
653
+ await configureTaskScheduler(coreConfig, envVars);
654
+ await configureEventBus(coreConfig, envVars);
655
+ await configureTelemetry(coreConfig, envVars);
656
+ await configureMessaging(coreConfig, envVars);
657
+ await configureFaucet(coreConfig, envVars);
658
+ await configureWallet(coreConfig, envVars);
659
+ await configureNft(coreConfig, envVars);
660
+ await configureVerifiableStorage(coreConfig, envVars);
661
+ await configureIdentity(coreConfig, envVars);
662
+ await configureIdentityResolver(coreConfig, envVars);
663
+ await configureIdentityProfile(coreConfig, envVars);
664
+ await configureAttestation(coreConfig, envVars);
665
+ await configureDataProcessing(coreConfig, envVars);
666
+ await configureAuditableItemGraph(coreConfig, envVars);
667
+ await configureAuditableItemStream(coreConfig, envVars);
668
+ await configureDocumentManagement(coreConfig, envVars);
669
+ await configureVerifiableCredentialAuthentication(coreConfig, envVars);
670
+ await configureRightsManagement(coreConfig, envVars);
671
+ await configureSynchronisedStorage(coreConfig, envVars);
672
+ await configureFederatedCatalogue(coreConfig, envVars);
673
+ await configureDataSpaceConnector(coreConfig, envVars);
565
674
  return coreConfig;
566
675
  }
567
- /**
568
- * Helper function to get IOTA configuration from centralized dltConfig.
569
- * @param coreConfig The core config.
570
- * @returns The IOTA configuration if found, undefined otherwise.
571
- */
572
- function getIotaConfig(coreConfig) {
573
- const dltConfig = coreConfig.types.dltConfig?.find(config => config.type === engineTypes.DltConfigType.Iota && config.isDefault);
574
- return dltConfig?.options?.config;
575
- }
576
676
  /**
577
677
  * Configures the entity storage.
578
678
  * @param coreConfig The core config.
579
679
  * @param envVars The environment variables.
580
680
  */
581
- function configureEntityStorage(coreConfig, envVars) {
681
+ async function configureEntityStorage(coreConfig, envVars) {
582
682
  coreConfig.types ??= {};
583
683
  coreConfig.types.entityStorageConnector ??= [];
584
684
  const entityStorageConnectorTypes = envVars.entityStorageConnectorType?.split(",") ?? [];
@@ -602,9 +702,10 @@ function configureEntityStorage(coreConfig, envVars) {
602
702
  options: {
603
703
  config: {
604
704
  region: envVars.awsDynamodbRegion ?? "",
605
- accessKeyId: envVars.awsDynamodbAccessKeyId ?? "",
606
- secretAccessKey: envVars.awsDynamodbSecretAccessKey ?? "",
607
- endpoint: envVars.awsDynamodbEndpoint ?? ""
705
+ authMode: envVars.awsDynamodbAuthMode,
706
+ accessKeyId: envVars.awsDynamodbAccessKeyId,
707
+ secretAccessKey: envVars.awsDynamodbSecretAccessKey,
708
+ endpoint: envVars.awsDynamodbEndpoint
608
709
  },
609
710
  tablePrefix: envVars.entityStorageTablePrefix
610
711
  }
@@ -683,7 +784,7 @@ function configureEntityStorage(coreConfig, envVars) {
683
784
  }
684
785
  });
685
786
  }
686
- if (entityStorageConnectorTypes) {
787
+ if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.PostgreSql)) {
687
788
  coreConfig.types.entityStorageConnector.push({
688
789
  type: engineTypes.EntityStorageConnectorType.PostgreSql,
689
790
  options: {
@@ -698,10 +799,19 @@ function configureEntityStorage(coreConfig, envVars) {
698
799
  }
699
800
  });
700
801
  }
802
+ const defaultEntityStorageConnectorType = envVars.entityStorageConnectorDefault ?? entityStorageConnectorTypes[0];
803
+ if (entityStorageConnectorTypes.includes(engineTypes.EntityStorageConnectorType.Synchronised)) {
804
+ // For synchronised storage we use the default connector as the one we wrap for real DB operations
805
+ coreConfig.types.entityStorageConnector.push({
806
+ type: engineTypes.EntityStorageConnectorType.Synchronised,
807
+ options: {
808
+ entityStorageConnectorType: defaultEntityStorageConnectorType
809
+ }
810
+ });
811
+ }
701
812
  if (core.Is.arrayValue(entityStorageConnectorTypes)) {
702
- const defaultStorageConnectorType = envVars.entityStorageConnectorDefault ?? entityStorageConnectorTypes[0];
703
813
  for (const config of coreConfig.types.entityStorageConnector) {
704
- if (config.type === defaultStorageConnectorType) {
814
+ if (config.type === defaultEntityStorageConnectorType) {
705
815
  config.isDefault = true;
706
816
  break;
707
817
  }
@@ -713,7 +823,7 @@ function configureEntityStorage(coreConfig, envVars) {
713
823
  * @param coreConfig The core config.
714
824
  * @param envVars The environment variables.
715
825
  */
716
- function configureBlobStorage(coreConfig, envVars) {
826
+ async function configureBlobStorage(coreConfig, envVars) {
717
827
  coreConfig.types.blobStorageConnector ??= [];
718
828
  const blobStorageConnectorTypes = envVars.blobStorageConnectorType?.split(",") ?? [];
719
829
  if (blobStorageConnectorTypes.includes(engineTypes.BlobStorageConnectorType.Memory)) {
@@ -752,9 +862,10 @@ function configureBlobStorage(coreConfig, envVars) {
752
862
  config: {
753
863
  region: envVars.awsS3Region ?? "",
754
864
  bucketName: envVars.awsS3BucketName ?? "",
755
- accessKeyId: envVars.awsS3AccessKeyId ?? "",
756
- secretAccessKey: envVars.awsS3SecretAccessKey ?? "",
757
- endpoint: envVars.awsS3Endpoint ?? ""
865
+ authMode: envVars.awsS3AuthMode,
866
+ accessKeyId: envVars.awsS3AccessKeyId,
867
+ secretAccessKey: envVars.awsS3SecretAccessKey,
868
+ endpoint: envVars.awsS3Endpoint
758
869
  },
759
870
  storagePrefix: envVars.blobStoragePrefix
760
871
  }
@@ -793,6 +904,13 @@ function configureBlobStorage(coreConfig, envVars) {
793
904
  for (const config of coreConfig.types.blobStorageConnector) {
794
905
  if (config.type === defaultStorageConnectorType) {
795
906
  config.isDefault = true;
907
+ }
908
+ // If this blob storage connector is the one to use for public access
909
+ // then add it as a feature
910
+ if (core.Is.stringValue(envVars.blobStorageConnectorPublic) &&
911
+ config.type === envVars.blobStorageConnectorPublic) {
912
+ config.features ??= [];
913
+ config.features.push("public");
796
914
  break;
797
915
  }
798
916
  }
@@ -804,7 +922,7 @@ function configureBlobStorage(coreConfig, envVars) {
804
922
  options: {
805
923
  config: {
806
924
  vaultKeyId: (envVars.blobStorageEnableEncryption ?? false)
807
- ? envVars.blobStorageEncryptionKey
925
+ ? (envVars.blobStorageEncryptionKeyId ?? BLOB_STORAGE_ENCRYPTION_KEY_ID)
808
926
  : undefined
809
927
  }
810
928
  }
@@ -816,41 +934,43 @@ function configureBlobStorage(coreConfig, envVars) {
816
934
  * @param coreConfig The core config.
817
935
  * @param envVars The environment variables.
818
936
  */
819
- function configureLogging(coreConfig, envVars) {
937
+ async function configureLogging(coreConfig, envVars) {
820
938
  coreConfig.types.loggingConnector ??= [];
821
- const loggingConnectors = (envVars.loggingConnector ?? "").split(",");
822
- for (const loggingConnector of loggingConnectors) {
939
+ const loggingConnectorTypes = (envVars.loggingConnector ?? "").split(",");
940
+ let additionalConnectorCount = 0;
941
+ for (const loggingConnector of loggingConnectorTypes) {
823
942
  if (loggingConnector === engineTypes.LoggingConnectorType.Console) {
824
- coreConfig.types.loggingConnector?.push({
943
+ coreConfig.types.loggingConnector.push({
825
944
  type: engineTypes.LoggingConnectorType.Console,
826
945
  options: {
827
946
  config: {
828
947
  translateMessages: true,
829
948
  hideGroups: true
830
949
  }
831
- },
832
- isDefault: loggingConnectors.length === 1
950
+ }
833
951
  });
952
+ additionalConnectorCount++;
834
953
  }
835
954
  else if (loggingConnector === engineTypes.LoggingConnectorType.EntityStorage) {
836
- coreConfig.types.loggingConnector?.push({
837
- type: engineTypes.LoggingConnectorType.EntityStorage,
838
- isDefault: loggingConnectors.length === 1
955
+ coreConfig.types.loggingConnector.push({
956
+ type: engineTypes.LoggingConnectorType.EntityStorage
839
957
  });
958
+ additionalConnectorCount++;
840
959
  }
841
960
  }
842
- if (loggingConnectors.length > 1) {
961
+ if (additionalConnectorCount > 1) {
843
962
  coreConfig.types.loggingConnector?.push({
844
963
  type: engineTypes.LoggingConnectorType.Multi,
845
- isDefault: true,
846
964
  options: {
847
- loggingConnectorTypes: loggingConnectors
965
+ loggingConnectorTypes
848
966
  }
849
967
  });
850
968
  }
851
- if (loggingConnectors.length > 0) {
969
+ if (additionalConnectorCount > 0) {
852
970
  coreConfig.types.loggingComponent ??= [];
853
- coreConfig.types.loggingComponent.push({ type: engineTypes.LoggingComponentType.Service });
971
+ // We set the isDefault flag so that other components will get this service by default
972
+ // and not the generic one from the engine core
973
+ coreConfig.types.loggingComponent.push({ type: engineTypes.LoggingComponentType.Service, isDefault: true });
854
974
  }
855
975
  }
856
976
  /**
@@ -858,7 +978,7 @@ function configureLogging(coreConfig, envVars) {
858
978
  * @param coreConfig The core config.
859
979
  * @param envVars The environment variables.
860
980
  */
861
- function configureVault(coreConfig, envVars) {
981
+ async function configureVault(coreConfig, envVars) {
862
982
  coreConfig.types.vaultConnector ??= [];
863
983
  if (envVars.vaultConnector === engineTypes.VaultConnectorType.EntityStorage) {
864
984
  coreConfig.types.vaultConnector.push({
@@ -882,7 +1002,7 @@ function configureVault(coreConfig, envVars) {
882
1002
  * @param coreConfig The core config.
883
1003
  * @param envVars The environment variables.
884
1004
  */
885
- function configureBackgroundTask(coreConfig, envVars) {
1005
+ async function configureBackgroundTask(coreConfig, envVars) {
886
1006
  coreConfig.types.backgroundTaskConnector ??= [];
887
1007
  if (envVars.backgroundTaskConnector === engineTypes.BackgroundTaskConnectorType.EntityStorage) {
888
1008
  coreConfig.types.backgroundTaskConnector.push({
@@ -895,7 +1015,7 @@ function configureBackgroundTask(coreConfig, envVars) {
895
1015
  * @param coreConfig The core config.
896
1016
  * @param envVars The environment variables.
897
1017
  */
898
- function configureEventBus(coreConfig, envVars) {
1018
+ async function configureEventBus(coreConfig, envVars) {
899
1019
  coreConfig.types.eventBusConnector ??= [];
900
1020
  if (envVars.eventBusConnector === engineTypes.EventBusConnectorType.Local) {
901
1021
  coreConfig.types.eventBusConnector.push({
@@ -912,7 +1032,7 @@ function configureEventBus(coreConfig, envVars) {
912
1032
  * @param coreConfig The core config.
913
1033
  * @param envVars The environment variables.
914
1034
  */
915
- function configureTelemetry(coreConfig, envVars) {
1035
+ async function configureTelemetry(coreConfig, envVars) {
916
1036
  coreConfig.types.telemetryConnector ??= [];
917
1037
  if (envVars.telemetryConnector === engineTypes.TelemetryConnectorType.EntityStorage) {
918
1038
  coreConfig.types.telemetryConnector.push({
@@ -929,71 +1049,84 @@ function configureTelemetry(coreConfig, envVars) {
929
1049
  * @param coreConfig The core config.
930
1050
  * @param envVars The environment variables.
931
1051
  */
932
- function configureMessaging(coreConfig, envVars) {
933
- coreConfig.types.messagingEmailConnector ??= [];
934
- coreConfig.types.messagingSmsConnector ??= [];
935
- coreConfig.types.messagingPushNotificationConnector ??= [];
936
- if (envVars.messagingEmailConnector === engineTypes.MessagingEmailConnectorType.EntityStorage) {
937
- coreConfig.types.messagingEmailConnector.push({
938
- type: engineTypes.MessagingEmailConnectorType.EntityStorage
939
- });
940
- }
941
- else if (envVars.messagingEmailConnector === engineTypes.MessagingEmailConnectorType.Aws) {
942
- coreConfig.types.messagingEmailConnector.push({
943
- type: engineTypes.MessagingEmailConnectorType.Aws,
944
- options: {
945
- config: {
946
- region: envVars.awsS3Region ?? "",
947
- accessKeyId: envVars.awsS3AccessKeyId ?? "",
948
- secretAccessKey: envVars.awsS3SecretAccessKey ?? "",
949
- endpoint: envVars.awsS3Endpoint ?? ""
1052
+ async function configureMessaging(coreConfig, envVars) {
1053
+ if (core.Coerce.boolean(envVars.messagingEnabled) ?? false) {
1054
+ coreConfig.types.messagingEmailConnector ??= [];
1055
+ coreConfig.types.messagingSmsConnector ??= [];
1056
+ coreConfig.types.messagingPushNotificationConnector ??= [];
1057
+ if (envVars.messagingEmailConnector === engineTypes.MessagingEmailConnectorType.EntityStorage) {
1058
+ coreConfig.types.messagingEmailConnector.push({
1059
+ type: engineTypes.MessagingEmailConnectorType.EntityStorage
1060
+ });
1061
+ }
1062
+ else if (envVars.messagingEmailConnector === engineTypes.MessagingEmailConnectorType.Aws) {
1063
+ coreConfig.types.messagingEmailConnector.push({
1064
+ type: engineTypes.MessagingEmailConnectorType.Aws,
1065
+ options: {
1066
+ config: {
1067
+ region: envVars.awsSesRegion ?? "",
1068
+ authMode: envVars.awsSesAuthMode,
1069
+ accessKeyId: envVars.awsSesAccessKeyId,
1070
+ secretAccessKey: envVars.awsSesSecretAccessKey,
1071
+ endpoint: envVars.awsSesEndpoint
1072
+ }
950
1073
  }
951
- }
952
- });
953
- }
954
- if (envVars.messagingSmsConnector === engineTypes.MessagingSmsConnectorType.EntityStorage) {
955
- coreConfig.types.messagingSmsConnector.push({
956
- type: engineTypes.MessagingSmsConnectorType.EntityStorage
957
- });
958
- }
959
- else if (envVars.messagingSmsConnector === engineTypes.MessagingSmsConnectorType.Aws) {
960
- coreConfig.types.messagingSmsConnector.push({
961
- type: engineTypes.MessagingSmsConnectorType.Aws,
962
- options: {
963
- config: {
964
- region: envVars.awsS3Region ?? "",
965
- accessKeyId: envVars.awsS3AccessKeyId ?? "",
966
- secretAccessKey: envVars.awsS3SecretAccessKey ?? "",
967
- endpoint: envVars.awsS3Endpoint ?? ""
1074
+ });
1075
+ }
1076
+ if (envVars.messagingSmsConnector === engineTypes.MessagingSmsConnectorType.EntityStorage) {
1077
+ coreConfig.types.messagingSmsConnector.push({
1078
+ type: engineTypes.MessagingSmsConnectorType.EntityStorage
1079
+ });
1080
+ }
1081
+ else if (envVars.messagingSmsConnector === engineTypes.MessagingSmsConnectorType.Aws) {
1082
+ coreConfig.types.messagingSmsConnector.push({
1083
+ type: engineTypes.MessagingSmsConnectorType.Aws,
1084
+ options: {
1085
+ config: {
1086
+ region: envVars.awsSesRegion ?? "",
1087
+ authMode: envVars.awsSesAuthMode,
1088
+ accessKeyId: envVars.awsSesAccessKeyId,
1089
+ secretAccessKey: envVars.awsSesSecretAccessKey,
1090
+ endpoint: envVars.awsSesEndpoint
1091
+ }
968
1092
  }
969
- }
970
- });
971
- }
972
- if (envVars.messagingPushNotificationConnector ===
973
- engineTypes.MessagingPushNotificationConnectorType.EntityStorage) {
974
- coreConfig.types.messagingPushNotificationConnector.push({
975
- type: engineTypes.MessagingPushNotificationConnectorType.EntityStorage
976
- });
977
- }
978
- else if (envVars.messagingPushNotificationConnector === engineTypes.MessagingPushNotificationConnectorType.Aws) {
979
- coreConfig.types.messagingPushNotificationConnector.push({
980
- type: engineTypes.MessagingPushNotificationConnectorType.Aws,
1093
+ });
1094
+ }
1095
+ if (envVars.messagingPushNotificationConnector ===
1096
+ engineTypes.MessagingPushNotificationConnectorType.EntityStorage) {
1097
+ coreConfig.types.messagingPushNotificationConnector.push({
1098
+ type: engineTypes.MessagingPushNotificationConnectorType.EntityStorage
1099
+ });
1100
+ }
1101
+ else if (envVars.messagingPushNotificationConnector === engineTypes.MessagingPushNotificationConnectorType.Aws) {
1102
+ coreConfig.types.messagingPushNotificationConnector.push({
1103
+ type: engineTypes.MessagingPushNotificationConnectorType.Aws,
1104
+ options: {
1105
+ config: {
1106
+ region: envVars.awsSesRegion ?? "",
1107
+ authMode: envVars.awsSesAuthMode,
1108
+ accessKeyId: envVars.awsSesAccessKeyId,
1109
+ secretAccessKey: envVars.awsSesSecretAccessKey,
1110
+ endpoint: envVars.awsSesEndpoint,
1111
+ applicationsSettings: core.Is.json(envVars.awsMessagingPushNotificationApplications)
1112
+ ? JSON.parse(envVars.awsMessagingPushNotificationApplications)
1113
+ : []
1114
+ }
1115
+ }
1116
+ });
1117
+ }
1118
+ const templates = core.Is.arrayValue(envVars.messagingTemplates)
1119
+ ? envVars.messagingTemplates
1120
+ : undefined;
1121
+ coreConfig.types.messagingAdminComponent ??= [];
1122
+ coreConfig.types.messagingAdminComponent.push({
1123
+ type: engineTypes.MessagingAdminComponentType.Service,
981
1124
  options: {
982
1125
  config: {
983
- region: envVars.awsS3Region ?? "",
984
- accessKeyId: envVars.awsS3AccessKeyId ?? "",
985
- secretAccessKey: envVars.awsS3SecretAccessKey ?? "",
986
- endpoint: envVars.awsS3Endpoint ?? "",
987
- applicationsSettings: core.Is.json(envVars.awsMessagingPushNotificationApplications)
988
- ? JSON.parse(envVars.awsMessagingPushNotificationApplications)
989
- : []
1126
+ templates
990
1127
  }
991
1128
  }
992
1129
  });
993
- }
994
- if (coreConfig.types.messagingEmailConnector.length > 0 ||
995
- coreConfig.types.messagingSmsConnector.length > 0 ||
996
- coreConfig.types.messagingPushNotificationConnector.length > 0) {
997
1130
  coreConfig.types.messagingComponent ??= [];
998
1131
  coreConfig.types.messagingComponent.push({ type: engineTypes.MessagingComponentType.Service });
999
1132
  }
@@ -1003,7 +1136,7 @@ function configureMessaging(coreConfig, envVars) {
1003
1136
  * @param coreConfig The core config.
1004
1137
  * @param envVars The environment variables.
1005
1138
  */
1006
- function configureFaucet(coreConfig, envVars) {
1139
+ async function configureFaucet(coreConfig, envVars) {
1007
1140
  coreConfig.types.faucetConnector ??= [];
1008
1141
  if (envVars.faucetConnector === engineTypes.FaucetConnectorType.EntityStorage) {
1009
1142
  coreConfig.types.faucetConnector.push({
@@ -1011,14 +1144,14 @@ function configureFaucet(coreConfig, envVars) {
1011
1144
  });
1012
1145
  }
1013
1146
  else if (envVars.faucetConnector === engineTypes.FaucetConnectorType.Iota) {
1014
- const iotaConfig = getIotaConfig(coreConfig);
1147
+ const dltConfig = engineTypes.EngineTypeHelper.getConfigOfType(coreConfig, "dltConfig", engineTypes.DltConfigType.Iota);
1015
1148
  coreConfig.types.faucetConnector.push({
1016
1149
  type: engineTypes.FaucetConnectorType.Iota,
1017
1150
  options: {
1018
1151
  config: {
1019
1152
  endpoint: envVars.iotaFaucetEndpoint ?? "",
1020
- clientOptions: iotaConfig?.clientOptions ?? { url: "" },
1021
- network: iotaConfig?.network ?? ""
1153
+ clientOptions: dltConfig?.options?.config?.clientOptions ?? { url: "" },
1154
+ network: dltConfig?.options?.config?.network ?? ""
1022
1155
  }
1023
1156
  }
1024
1157
  });
@@ -1029,7 +1162,7 @@ function configureFaucet(coreConfig, envVars) {
1029
1162
  * @param coreConfig The core config.
1030
1163
  * @param envVars The environment variables.
1031
1164
  */
1032
- function configureWallet(coreConfig, envVars) {
1165
+ async function configureWallet(coreConfig, envVars) {
1033
1166
  coreConfig.types.walletConnector ??= [];
1034
1167
  if (envVars.walletConnector === engineTypes.WalletConnectorType.EntityStorage) {
1035
1168
  coreConfig.types.walletConnector.push({
@@ -1037,11 +1170,11 @@ function configureWallet(coreConfig, envVars) {
1037
1170
  });
1038
1171
  }
1039
1172
  else if (envVars.walletConnector === engineTypes.WalletConnectorType.Iota) {
1040
- const iotaConfig = getIotaConfig(coreConfig);
1173
+ const dltConfig = engineTypes.EngineTypeHelper.getConfigOfType(coreConfig, "dltConfig", engineTypes.DltConfigType.Iota);
1041
1174
  coreConfig.types.walletConnector.push({
1042
1175
  type: engineTypes.WalletConnectorType.Iota,
1043
1176
  options: {
1044
- config: iotaConfig ?? {}
1177
+ config: dltConfig?.options?.config ?? {}
1045
1178
  }
1046
1179
  });
1047
1180
  }
@@ -1051,7 +1184,7 @@ function configureWallet(coreConfig, envVars) {
1051
1184
  * @param coreConfig The core config.
1052
1185
  * @param envVars The environment variables.
1053
1186
  */
1054
- function configureNft(coreConfig, envVars) {
1187
+ async function configureNft(coreConfig, envVars) {
1055
1188
  coreConfig.types.nftConnector ??= [];
1056
1189
  if (envVars.nftConnector === engineTypes.NftConnectorType.EntityStorage) {
1057
1190
  coreConfig.types.nftConnector.push({
@@ -1059,11 +1192,11 @@ function configureNft(coreConfig, envVars) {
1059
1192
  });
1060
1193
  }
1061
1194
  else if (envVars.nftConnector === engineTypes.NftConnectorType.Iota) {
1062
- const iotaConfig = getIotaConfig(coreConfig);
1195
+ const dltConfig = engineTypes.EngineTypeHelper.getConfigOfType(coreConfig, "dltConfig", engineTypes.DltConfigType.Iota);
1063
1196
  coreConfig.types.nftConnector.push({
1064
1197
  type: engineTypes.NftConnectorType.Iota,
1065
1198
  options: {
1066
- config: iotaConfig ?? {}
1199
+ config: dltConfig?.options?.config ?? {}
1067
1200
  }
1068
1201
  });
1069
1202
  }
@@ -1077,7 +1210,7 @@ function configureNft(coreConfig, envVars) {
1077
1210
  * @param coreConfig The core config.
1078
1211
  * @param envVars The environment variables.
1079
1212
  */
1080
- function configureVerifiableStorage(coreConfig, envVars) {
1213
+ async function configureVerifiableStorage(coreConfig, envVars) {
1081
1214
  coreConfig.types.verifiableStorageConnector ??= [];
1082
1215
  if (envVars.verifiableStorageConnector === engineTypes.VerifiableStorageConnectorType.EntityStorage) {
1083
1216
  coreConfig.types.verifiableStorageConnector.push({
@@ -1085,11 +1218,11 @@ function configureVerifiableStorage(coreConfig, envVars) {
1085
1218
  });
1086
1219
  }
1087
1220
  else if (envVars.verifiableStorageConnector === engineTypes.VerifiableStorageConnectorType.Iota) {
1088
- const iotaConfig = getIotaConfig(coreConfig);
1221
+ const dltConfig = engineTypes.EngineTypeHelper.getConfigOfType(coreConfig, "dltConfig", engineTypes.DltConfigType.Iota);
1089
1222
  coreConfig.types.verifiableStorageConnector.push({
1090
1223
  type: engineTypes.VerifiableStorageConnectorType.Iota,
1091
1224
  options: {
1092
- config: iotaConfig ?? {}
1225
+ config: dltConfig?.options?.config ?? {}
1093
1226
  }
1094
1227
  });
1095
1228
  }
@@ -1103,18 +1236,10 @@ function configureVerifiableStorage(coreConfig, envVars) {
1103
1236
  type: engineTypes.ImmutableProofComponentType.Service,
1104
1237
  options: {
1105
1238
  config: {
1106
- verificationMethodId: envVars.immutableProofVerificationMethodId
1239
+ verificationMethodId: envVars.immutableProofVerificationMethodId ?? IMMUTABLE_PROOF_VERIFICATION_METHOD_ID
1107
1240
  }
1108
1241
  }
1109
1242
  });
1110
- coreConfig.types.auditableItemGraphComponent ??= [];
1111
- coreConfig.types.auditableItemGraphComponent.push({
1112
- type: engineTypes.AuditableItemGraphComponentType.Service
1113
- });
1114
- coreConfig.types.auditableItemStreamComponent ??= [];
1115
- coreConfig.types.auditableItemStreamComponent.push({
1116
- type: engineTypes.AuditableItemStreamComponentType.Service
1117
- });
1118
1243
  }
1119
1244
  }
1120
1245
  /**
@@ -1122,7 +1247,7 @@ function configureVerifiableStorage(coreConfig, envVars) {
1122
1247
  * @param coreConfig The core config.
1123
1248
  * @param envVars The environment variables.
1124
1249
  */
1125
- function configureIdentity(coreConfig, envVars) {
1250
+ async function configureIdentity(coreConfig, envVars) {
1126
1251
  coreConfig.types.identityConnector ??= [];
1127
1252
  if (envVars.identityConnector === engineTypes.IdentityConnectorType.EntityStorage) {
1128
1253
  coreConfig.types.identityConnector.push({
@@ -1130,11 +1255,11 @@ function configureIdentity(coreConfig, envVars) {
1130
1255
  });
1131
1256
  }
1132
1257
  else if (envVars.identityConnector === engineTypes.IdentityConnectorType.Iota) {
1133
- const iotaConfig = getIotaConfig(coreConfig);
1258
+ const dltConfig = engineTypes.EngineTypeHelper.getConfigOfType(coreConfig, "dltConfig", engineTypes.DltConfigType.Iota);
1134
1259
  coreConfig.types.identityConnector.push({
1135
1260
  type: engineTypes.IdentityConnectorType.Iota,
1136
1261
  options: {
1137
- config: iotaConfig ?? {}
1262
+ config: dltConfig?.options?.config ?? {}
1138
1263
  }
1139
1264
  });
1140
1265
  }
@@ -1148,7 +1273,7 @@ function configureIdentity(coreConfig, envVars) {
1148
1273
  * @param coreConfig The core config.
1149
1274
  * @param envVars The environment variables.
1150
1275
  */
1151
- function configureIdentityResolver(coreConfig, envVars) {
1276
+ async function configureIdentityResolver(coreConfig, envVars) {
1152
1277
  coreConfig.types.identityResolverConnector ??= [];
1153
1278
  if (envVars.identityResolverConnector === engineTypes.IdentityResolverConnectorType.EntityStorage) {
1154
1279
  coreConfig.types.identityResolverConnector.push({
@@ -1156,11 +1281,11 @@ function configureIdentityResolver(coreConfig, envVars) {
1156
1281
  });
1157
1282
  }
1158
1283
  else if (envVars.identityResolverConnector === engineTypes.IdentityResolverConnectorType.Iota) {
1159
- const iotaConfig = getIotaConfig(coreConfig);
1284
+ const dltConfig = engineTypes.EngineTypeHelper.getConfigOfType(coreConfig, "dltConfig", engineTypes.DltConfigType.Iota);
1160
1285
  coreConfig.types.identityResolverConnector.push({
1161
1286
  type: engineTypes.IdentityResolverConnectorType.Iota,
1162
1287
  options: {
1163
- config: iotaConfig ?? {}
1288
+ config: dltConfig?.options?.config ?? {}
1164
1289
  }
1165
1290
  });
1166
1291
  }
@@ -1186,7 +1311,7 @@ function configureIdentityResolver(coreConfig, envVars) {
1186
1311
  * @param coreConfig The core config.
1187
1312
  * @param envVars The environment variables.
1188
1313
  */
1189
- function configureIdentityProfile(coreConfig, envVars) {
1314
+ async function configureIdentityProfile(coreConfig, envVars) {
1190
1315
  coreConfig.types.identityProfileConnector ??= [];
1191
1316
  if (envVars.identityProfileConnector === engineTypes.IdentityConnectorType.EntityStorage) {
1192
1317
  coreConfig.types.identityProfileConnector.push({
@@ -1203,7 +1328,7 @@ function configureIdentityProfile(coreConfig, envVars) {
1203
1328
  * @param coreConfig The core config.
1204
1329
  * @param envVars The environment variables.
1205
1330
  */
1206
- function configureAttestation(coreConfig, envVars) {
1331
+ async function configureAttestation(coreConfig, envVars) {
1207
1332
  coreConfig.types.attestationConnector ??= [];
1208
1333
  if (envVars.attestationConnector === engineTypes.AttestationConnectorType.Nft) {
1209
1334
  coreConfig.types.attestationConnector.push({
@@ -1216,7 +1341,7 @@ function configureAttestation(coreConfig, envVars) {
1216
1341
  type: engineTypes.AttestationComponentType.Service,
1217
1342
  options: {
1218
1343
  config: {
1219
- verificationMethodId: envVars.attestationVerificationMethodId
1344
+ verificationMethodId: envVars.attestationVerificationMethodId ?? ATTESTATION_VERIFICATION_METHOD_ID
1220
1345
  }
1221
1346
  }
1222
1347
  });
@@ -1227,8 +1352,8 @@ function configureAttestation(coreConfig, envVars) {
1227
1352
  * @param coreConfig The core config.
1228
1353
  * @param envVars The environment variables.
1229
1354
  */
1230
- function configureAuditableItemGraph(coreConfig, envVars) {
1231
- if (core.Is.arrayValue(coreConfig.types.verifiableStorageConnector)) {
1355
+ async function configureAuditableItemGraph(coreConfig, envVars) {
1356
+ if (core.Coerce.boolean(envVars.auditableItemGraphEnabled) ?? false) {
1232
1357
  coreConfig.types.auditableItemGraphComponent ??= [];
1233
1358
  coreConfig.types.auditableItemGraphComponent.push({
1234
1359
  type: engineTypes.AuditableItemGraphComponentType.Service
@@ -1240,8 +1365,8 @@ function configureAuditableItemGraph(coreConfig, envVars) {
1240
1365
  * @param coreConfig The core config.
1241
1366
  * @param envVars The environment variables.
1242
1367
  */
1243
- function configureAuditableItemStream(coreConfig, envVars) {
1244
- if (core.Is.arrayValue(coreConfig.types.verifiableStorageConnector)) {
1368
+ async function configureAuditableItemStream(coreConfig, envVars) {
1369
+ if (core.Coerce.boolean(envVars.auditableItemStreamEnabled) ?? false) {
1245
1370
  coreConfig.types.auditableItemStreamComponent ??= [];
1246
1371
  coreConfig.types.auditableItemStreamComponent.push({
1247
1372
  type: engineTypes.AuditableItemStreamComponentType.Service
@@ -1253,34 +1378,33 @@ function configureAuditableItemStream(coreConfig, envVars) {
1253
1378
  * @param coreConfig The core config.
1254
1379
  * @param envVars The environment variables.
1255
1380
  */
1256
- function configureDataProcessing(coreConfig, envVars) {
1257
- coreConfig.types.dataConverterConnector ??= [];
1258
- coreConfig.types.dataExtractorConnector ??= [];
1259
- const converterConnectors = envVars.dataConverterConnectors?.split(",") ?? [];
1260
- for (const converterConnector of converterConnectors) {
1261
- if (converterConnector === engineTypes.DataConverterConnectorType.Json) {
1262
- coreConfig.types.dataConverterConnector.push({
1263
- type: engineTypes.DataConverterConnectorType.Json
1264
- });
1265
- }
1266
- else if (converterConnector === engineTypes.DataConverterConnectorType.Xml) {
1267
- coreConfig.types.dataConverterConnector.push({
1268
- type: engineTypes.DataConverterConnectorType.Xml
1269
- });
1270
- }
1271
- }
1272
- const extractorConnectors = envVars.dataExtractorConnectors?.split(",") ?? [];
1273
- for (const extractorConnector of extractorConnectors) {
1274
- if (extractorConnector === engineTypes.DataExtractorConnectorType.JsonPath) {
1275
- coreConfig.types.dataExtractorConnector.push({
1276
- type: engineTypes.DataExtractorConnectorType.JsonPath
1277
- });
1278
- }
1279
- }
1280
- if (coreConfig.types.dataConverterConnector.length > 0 ||
1281
- coreConfig.types.dataExtractorConnector.length > 0) {
1381
+ async function configureDataProcessing(coreConfig, envVars) {
1382
+ if (core.Coerce.boolean(envVars.dataProcessingEnabled) ?? false) {
1282
1383
  coreConfig.types.dataProcessingComponent ??= [];
1283
1384
  coreConfig.types.dataProcessingComponent.push({ type: engineTypes.DataProcessingComponentType.Service });
1385
+ coreConfig.types.dataConverterConnector ??= [];
1386
+ const converterConnectors = envVars.dataConverterConnectors?.split(",") ?? [];
1387
+ for (const converterConnector of converterConnectors) {
1388
+ if (converterConnector === engineTypes.DataConverterConnectorType.Json) {
1389
+ coreConfig.types.dataConverterConnector.push({
1390
+ type: engineTypes.DataConverterConnectorType.Json
1391
+ });
1392
+ }
1393
+ else if (converterConnector === engineTypes.DataConverterConnectorType.Xml) {
1394
+ coreConfig.types.dataConverterConnector.push({
1395
+ type: engineTypes.DataConverterConnectorType.Xml
1396
+ });
1397
+ }
1398
+ }
1399
+ coreConfig.types.dataExtractorConnector ??= [];
1400
+ const extractorConnectors = envVars.dataExtractorConnectors?.split(",") ?? [];
1401
+ for (const extractorConnector of extractorConnectors) {
1402
+ if (extractorConnector === engineTypes.DataExtractorConnectorType.JsonPath) {
1403
+ coreConfig.types.dataExtractorConnector.push({
1404
+ type: engineTypes.DataExtractorConnectorType.JsonPath
1405
+ });
1406
+ }
1407
+ }
1284
1408
  }
1285
1409
  }
1286
1410
  /**
@@ -1288,10 +1412,8 @@ function configureDataProcessing(coreConfig, envVars) {
1288
1412
  * @param coreConfig The core config.
1289
1413
  * @param envVars The environment variables.
1290
1414
  */
1291
- function configureDocumentManagement(coreConfig, envVars) {
1292
- if (core.Is.arrayValue(coreConfig.types.auditableItemGraphComponent) &&
1293
- core.Is.arrayValue(coreConfig.types.blobStorageComponent) &&
1294
- core.Is.arrayValue(coreConfig.types.attestationComponent)) {
1415
+ async function configureDocumentManagement(coreConfig, envVars) {
1416
+ if (core.Coerce.boolean(envVars.documentManagementEnabled) ?? false) {
1295
1417
  coreConfig.types.documentManagementComponent ??= [];
1296
1418
  coreConfig.types.documentManagementComponent.push({
1297
1419
  type: engineTypes.DocumentManagementComponentType.Service
@@ -1299,21 +1421,22 @@ function configureDocumentManagement(coreConfig, envVars) {
1299
1421
  }
1300
1422
  }
1301
1423
  /**
1302
- * Configures the federated catalogue.
1424
+ * Configures the verifiable credential authentication.
1303
1425
  * @param coreConfig The core config.
1304
1426
  * @param envVars The environment variables.
1305
1427
  */
1306
- function configureFederatedCatalogue(coreConfig, envVars) {
1307
- if (core.Is.arrayValue(coreConfig.types.identityResolverComponent)) {
1308
- coreConfig.types.federatedCatalogueComponent ??= [];
1309
- coreConfig.types.federatedCatalogueComponent.push({
1310
- type: engineTypes.FederatedCatalogueComponentType.Service,
1428
+ async function configureVerifiableCredentialAuthentication(coreConfig, envVars) {
1429
+ if (core.Coerce.boolean(envVars.vcAuthenticationEnabled) ?? false) {
1430
+ // Can only perform VC authentication if identity component is available
1431
+ coreConfig.types.authenticationGeneratorComponent ??= [];
1432
+ coreConfig.types.authenticationGeneratorComponent.push({
1433
+ type: engineTypes.AuthenticationGeneratorComponentType.VerifiableCredential,
1311
1434
  options: {
1312
1435
  config: {
1313
- subResourceCacheTtlMs: core.Coerce.number(envVars.federatedCatalogueCacheTtlMs),
1314
- clearingHouseApproverList: core.Coerce.object(envVars.federatedCatalogueClearingHouseApproverList) ?? []
1436
+ verificationMethodId: envVars.vcAuthenticationVerificationMethodId ?? VC_AUTHENTICATION_VERIFICATION_METHOD_ID
1315
1437
  }
1316
- }
1438
+ },
1439
+ features: ["verifiable-credential"]
1317
1440
  });
1318
1441
  }
1319
1442
  }
@@ -1322,15 +1445,87 @@ function configureFederatedCatalogue(coreConfig, envVars) {
1322
1445
  * @param coreConfig The core config.
1323
1446
  * @param envVars The environment variables.
1324
1447
  */
1325
- function configureRightsManagement(coreConfig, envVars) {
1448
+ async function configureRightsManagement(coreConfig, envVars) {
1326
1449
  if (core.Coerce.boolean(envVars.rightsManagementEnabled) ?? false) {
1327
1450
  coreConfig.types.rightsManagementPapComponent ??= [];
1328
1451
  coreConfig.types.rightsManagementPapComponent.push({
1329
1452
  type: engineTypes.RightsManagementPapComponentType.Service
1330
1453
  });
1331
- coreConfig.types.rightsManagementComponent ??= [];
1332
- coreConfig.types.rightsManagementComponent.push({
1333
- type: engineTypes.RightsManagementComponentType.Service
1454
+ coreConfig.types.rightsManagementPmpComponent ??= [];
1455
+ coreConfig.types.rightsManagementPmpComponent.push({
1456
+ type: engineTypes.RightsManagementPmpComponentType.Service
1457
+ });
1458
+ coreConfig.types.rightsManagementPipComponent ??= [];
1459
+ coreConfig.types.rightsManagementPipComponent.push({
1460
+ type: engineTypes.RightsManagementPipComponentType.Service,
1461
+ options: {
1462
+ informationModulesConfig: core.Is.arrayValue(envVars.rightsManagementInformationSources)
1463
+ ? envVars.rightsManagementInformationSources
1464
+ : undefined
1465
+ }
1466
+ });
1467
+ coreConfig.types.rightsManagementPxpComponent ??= [];
1468
+ coreConfig.types.rightsManagementPxpComponent.push({
1469
+ type: engineTypes.RightsManagementPxpComponentType.Service,
1470
+ options: {
1471
+ actionModulesConfig: core.Is.arrayValue(envVars.rightsManagementExecutionActions)
1472
+ ? envVars.rightsManagementExecutionActions
1473
+ : undefined
1474
+ }
1475
+ });
1476
+ coreConfig.types.rightsManagementPdpComponent ??= [];
1477
+ coreConfig.types.rightsManagementPdpComponent.push({
1478
+ type: engineTypes.RightsManagementPdpComponentType.Service,
1479
+ options: {
1480
+ arbiterModulesConfig: core.Is.arrayValue(envVars.rightsManagementArbiters)
1481
+ ? envVars.rightsManagementArbiters
1482
+ : undefined
1483
+ }
1484
+ });
1485
+ coreConfig.types.rightsManagementPepComponent ??= [];
1486
+ coreConfig.types.rightsManagementPepComponent.push({
1487
+ type: engineTypes.RightsManagementPepComponentType.Service,
1488
+ options: {
1489
+ processorModulesConfig: core.Is.arrayValue(envVars.rightsManagementEnforcementProcessors)
1490
+ ? envVars.rightsManagementEnforcementProcessors
1491
+ : undefined
1492
+ }
1493
+ });
1494
+ coreConfig.types.rightsManagementPnpComponent ??= [];
1495
+ coreConfig.types.rightsManagementPnpComponent.push({
1496
+ type: engineTypes.RightsManagementPnpComponentType.Service,
1497
+ options: {
1498
+ negotiatorModulesConfig: core.Is.arrayValue(envVars.rightsManagementNegotiators)
1499
+ ? envVars.rightsManagementNegotiators
1500
+ : undefined,
1501
+ requesterModulesConfig: core.Is.arrayValue(envVars.rightsManagementRequesters)
1502
+ ? envVars.rightsManagementRequesters
1503
+ : undefined,
1504
+ config: {
1505
+ baseCallbackUrl: envVars.rightsManagementBaseCallbackUrl ?? "",
1506
+ offers: core.Is.arrayValue(envVars.rightsManagementOffers)
1507
+ ? envVars.rightsManagementOffers
1508
+ : [],
1509
+ negotiationComponentCreator: async (url) => new rightsManagementRestClient.PolicyNegotiationPointClient({ endpoint: url })
1510
+ }
1511
+ }
1512
+ });
1513
+ coreConfig.types.rightsManagementPnapComponent ??= [];
1514
+ coreConfig.types.rightsManagementPnapComponent.push({
1515
+ type: engineTypes.RightsManagementPnapComponentType.Service
1516
+ });
1517
+ coreConfig.types.rightsManagementDapComponent ??= [];
1518
+ coreConfig.types.rightsManagementDapComponent.push({
1519
+ type: engineTypes.RightsManagementDapComponentType.Service
1520
+ });
1521
+ coreConfig.types.rightsManagementDarpComponent ??= [];
1522
+ coreConfig.types.rightsManagementDarpComponent.push({
1523
+ type: engineTypes.RightsManagementDarpComponentType.Service,
1524
+ options: {
1525
+ config: {
1526
+ dataAccessComponentCreator: async (url) => new rightsManagementRestClient.DataAccessPointClient({ endpoint: url })
1527
+ }
1528
+ }
1334
1529
  });
1335
1530
  }
1336
1531
  }
@@ -1339,11 +1534,92 @@ function configureRightsManagement(coreConfig, envVars) {
1339
1534
  * @param coreConfig The core config.
1340
1535
  * @param envVars The environment variables.
1341
1536
  */
1342
- function configureTaskScheduler(coreConfig, envVars) {
1343
- if (core.Coerce.boolean(envVars.taskSchedulerEnabled) ?? true) {
1537
+ async function configureTaskScheduler(coreConfig, envVars) {
1538
+ if (core.Coerce.boolean(envVars.taskSchedulerEnabled) ?? false) {
1344
1539
  coreConfig.types.taskSchedulerComponent ??= [];
1345
1540
  coreConfig.types.taskSchedulerComponent.push({
1346
- type: engineTypes.TaskSchedulerComponentType.Default
1541
+ type: engineTypes.TaskSchedulerComponentType.Service
1542
+ });
1543
+ }
1544
+ }
1545
+ /**
1546
+ * Configures the synchronised storage.
1547
+ * @param coreConfig The core config.
1548
+ * @param envVars The environment variables.
1549
+ */
1550
+ async function configureSynchronisedStorage(coreConfig, envVars) {
1551
+ if (core.Is.arrayValue(coreConfig.types.identityResolverComponent) &&
1552
+ (core.Coerce.boolean(envVars.synchronisedStorageEnabled) ?? false)) {
1553
+ // Check if the config provides a custom verifiable storage key id
1554
+ let verifiableStorageKeyId = core.Coerce.string(envVars.synchronisedStorageVerifiableStorageKeyId);
1555
+ if (!core.Is.stringValue(verifiableStorageKeyId)) {
1556
+ // No custom key so default to the network setting
1557
+ verifiableStorageKeyId = envVars.iotaNetwork;
1558
+ }
1559
+ coreConfig.types.synchronisedStorageComponent ??= [];
1560
+ coreConfig.types.synchronisedStorageComponent.push({
1561
+ type: engineTypes.SynchronisedStorageComponentType.Service,
1562
+ options: {
1563
+ config: {
1564
+ verifiableStorageKeyId: verifiableStorageKeyId ?? "",
1565
+ blobStorageEncryptionKeyId: envVars.synchronisedStorageBlobStorageEncryptionKeyId ??
1566
+ SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID,
1567
+ entityUpdateIntervalMinutes: core.Coerce.number(envVars.synchronisedStorageEntityUpdateIntervalMinutes),
1568
+ consolidationIntervalMinutes: core.Coerce.number(envVars.synchronisedStorageConsolidationIntervalMinutes),
1569
+ consolidationBatchSize: core.Coerce.number(envVars.synchronisedStorageConsolidationBatchSize),
1570
+ maxConsolidations: core.Coerce.number(envVars.synchronisedStorageMaxConsolidations)
1571
+ }
1572
+ }
1573
+ });
1574
+ // If there is a trusted url set, we need to add a client
1575
+ // and give it a feature of trusted so that when the synchronised
1576
+ // storage is created it can pickup the correct component
1577
+ if (core.Is.stringValue(envVars.synchronisedStorageTrustedUrl)) {
1578
+ coreConfig.types.synchronisedStorageComponent.push({
1579
+ type: engineTypes.SynchronisedStorageComponentType.RestClient,
1580
+ options: {
1581
+ endpoint: envVars.synchronisedStorageTrustedUrl
1582
+ },
1583
+ features: ["trusted"]
1584
+ });
1585
+ }
1586
+ }
1587
+ }
1588
+ /**
1589
+ * Configures the federated catalogue.
1590
+ * @param coreConfig The core config.
1591
+ * @param envVars The environment variables.
1592
+ */
1593
+ async function configureFederatedCatalogue(coreConfig, envVars) {
1594
+ if (core.Coerce.boolean(envVars.federatedCatalogueEnabled) ?? false) {
1595
+ coreConfig.types.federatedCatalogueComponent ??= [];
1596
+ coreConfig.types.federatedCatalogueComponent.push({
1597
+ type: engineTypes.FederatedCatalogueComponentType.Service,
1598
+ options: {
1599
+ config: {
1600
+ subResourceCacheTtlMs: core.Coerce.number(envVars.federatedCatalogueCacheTtlMs),
1601
+ clearingHouseApproverList: core.Coerce.object(envVars.federatedCatalogueClearingHouseApproverList) ?? []
1602
+ }
1603
+ }
1604
+ });
1605
+ }
1606
+ }
1607
+ /**
1608
+ * Configures the data space connector.
1609
+ * @param coreConfig The core config.
1610
+ * @param envVars The environment variables.
1611
+ */
1612
+ async function configureDataSpaceConnector(coreConfig, envVars) {
1613
+ if (core.Coerce.boolean(envVars.dataSpaceConnectorEnabled) ?? false) {
1614
+ coreConfig.types.dataSpaceConnectorComponent ??= [];
1615
+ coreConfig.types.dataSpaceConnectorComponent.push({
1616
+ type: engineTypes.DataSpaceConnectorComponentType.Service,
1617
+ options: {
1618
+ config: {
1619
+ retainActivityLogsFor: core.Coerce.number(envVars.dataSpaceConnectorRetainActivityLogsFor),
1620
+ activityLogsCleanUpInterval: core.Coerce.number(envVars.dataSpaceConnectorActivityLogsCleanUpInterval)
1621
+ }
1622
+ }
1347
1623
  });
1348
1624
  }
1349
1625
  }
@@ -1352,7 +1628,7 @@ function configureTaskScheduler(coreConfig, envVars) {
1352
1628
  * @param coreConfig The core config.
1353
1629
  * @param envVars The environment variables.
1354
1630
  */
1355
- function configureDlt(coreConfig, envVars) {
1631
+ async function configureDlt(coreConfig, envVars) {
1356
1632
  // Create centralized DLT configuration for IOTA if essential IOTA variables are set
1357
1633
  if (core.Is.stringValue(envVars.iotaNodeEndpoint) && core.Is.stringValue(envVars.iotaNetwork)) {
1358
1634
  coreConfig.types.dltConfig ??= [];
@@ -1386,10 +1662,10 @@ function configureDlt(coreConfig, envVars) {
1386
1662
  * @param coreEngineConfig The core engine config.
1387
1663
  * @param serverInfo The server information.
1388
1664
  * @param openApiSpecPath The path to the open api spec.
1389
- * @returns The the config for the core and the server.
1665
+ * @param favIconPath The path to the favicon.
1666
+ * @returns The config for the core and the server.
1390
1667
  */
1391
- function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, openApiSpecPath) {
1392
- envVars.authSigningKeyId ??= "auth-signing";
1668
+ async function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, openApiSpecPath, favIconPath) {
1393
1669
  const webServerOptions = {
1394
1670
  port: core.Coerce.number(envVars.port),
1395
1671
  host: core.Coerce.string(envVars.host),
@@ -1415,7 +1691,8 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1415
1691
  options: {
1416
1692
  config: {
1417
1693
  serverInfo,
1418
- openApiSpecPath
1694
+ openApiSpecPath,
1695
+ favIconPath
1419
1696
  }
1420
1697
  }
1421
1698
  }
@@ -1435,8 +1712,9 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1435
1712
  }
1436
1713
  serverConfig.types.restRouteProcessor ??= [];
1437
1714
  serverConfig.types.socketRouteProcessor ??= [];
1438
- const disableNodeIdentity = core.Coerce.boolean(envVars.disableNodeIdentity);
1439
- if (!disableNodeIdentity) {
1715
+ const features = getFeatures(envVars);
1716
+ const hasNodeIdentity = features.includes(NodeFeatures.NodeIdentity);
1717
+ if (hasNodeIdentity) {
1440
1718
  serverConfig.types.restRouteProcessor.push({
1441
1719
  type: engineServerTypes.RestRouteProcessorType.NodeIdentity
1442
1720
  });
@@ -1445,11 +1723,18 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1445
1723
  });
1446
1724
  }
1447
1725
  if (!coreEngineConfig.silent) {
1726
+ const includeBody = core.Coerce.boolean(envVars.routeLoggingIncludeBody) ?? coreEngineConfig.debug;
1727
+ const fullBase64 = core.Coerce.boolean(envVars.routeLoggingFullBase64) ?? false;
1728
+ const obfuscateProperties = core.Is.stringValue(envVars.routeLoggingObfuscateProperties)
1729
+ ? envVars.routeLoggingObfuscateProperties.split(",")
1730
+ : undefined;
1448
1731
  serverConfig.types.restRouteProcessor.push({
1449
1732
  type: engineServerTypes.RestRouteProcessorType.Logging,
1450
1733
  options: {
1451
1734
  config: {
1452
- includeBody: coreEngineConfig.debug
1735
+ includeBody,
1736
+ fullBase64,
1737
+ obfuscateProperties
1453
1738
  }
1454
1739
  }
1455
1740
  });
@@ -1457,7 +1742,9 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1457
1742
  type: engineServerTypes.SocketRouteProcessorType.Logging,
1458
1743
  options: {
1459
1744
  config: {
1460
- includeBody: coreEngineConfig.debug
1745
+ includeBody,
1746
+ fullBase64,
1747
+ obfuscateProperties
1461
1748
  }
1462
1749
  }
1463
1750
  });
@@ -1495,7 +1782,7 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1495
1782
  type: engineServerTypes.AuthenticationComponentType.EntityStorage,
1496
1783
  options: {
1497
1784
  config: {
1498
- signingKeyName: envVars.authSigningKeyId
1785
+ signingKeyName: envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID
1499
1786
  }
1500
1787
  }
1501
1788
  });
@@ -1503,7 +1790,7 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1503
1790
  type: engineServerTypes.RestRouteProcessorType.AuthHeader,
1504
1791
  options: {
1505
1792
  config: {
1506
- signingKeyName: envVars.authSigningKeyId
1793
+ signingKeyName: envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID
1507
1794
  }
1508
1795
  }
1509
1796
  });
@@ -1511,11 +1798,19 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1511
1798
  type: engineServerTypes.SocketRouteProcessorType.AuthHeader,
1512
1799
  options: {
1513
1800
  config: {
1514
- signingKeyName: envVars.authSigningKeyId
1801
+ signingKeyName: envVars.authSigningKeyId ?? AUTH_SIGNING_KEY_ID
1515
1802
  }
1516
1803
  }
1517
1804
  });
1518
1805
  }
1806
+ if (core.Coerce.boolean(envVars.vcAuthenticationEnabled) ?? false) {
1807
+ serverConfig.types.restRouteProcessor.push({
1808
+ type: engineServerTypes.RestRouteProcessorType.AuthVerifiableCredential
1809
+ });
1810
+ serverConfig.types.socketRouteProcessor.push({
1811
+ type: engineServerTypes.SocketRouteProcessorType.AuthVerifiableCredential
1812
+ });
1813
+ }
1519
1814
  engineServer.addDefaultRestPaths(serverConfig);
1520
1815
  engineServer.addDefaultSocketPaths(serverConfig);
1521
1816
  return serverConfig;
@@ -1523,18 +1818,115 @@ function buildEngineServerConfiguration(envVars, coreEngineConfig, serverInfo, o
1523
1818
 
1524
1819
  // Copyright 2024 IOTA Stiftung.
1525
1820
  // SPDX-License-Identifier: Apache-2.0.
1526
- /* eslint-disable no-console */
1821
+ /**
1822
+ * Handles the configuration of the extensions.
1823
+ * @param envVars The environment variables for the node.
1824
+ * @param nodeEngineConfig The node engine config.
1825
+ * @returns The config for the core and the server.
1826
+ */
1827
+ async function extensionsConfiguration(envVars, nodeEngineConfig) {
1828
+ if (core.Is.stringValue(envVars.extensions)) {
1829
+ const extensions = envVars.extensions.split(",");
1830
+ for (const extension of extensions) {
1831
+ let initialiseConfigMethod;
1832
+ try {
1833
+ cliCore.CLIDisplay.value(core.I18n.formatMessage("node.extensionLoading"), extension);
1834
+ initialiseConfigMethod = await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialise");
1835
+ }
1836
+ catch (err) {
1837
+ throw new core.GeneralError("node", "extensionLoadingError", { extension }, err);
1838
+ }
1839
+ if (core.Is.function(initialiseConfigMethod)) {
1840
+ await initialiseConfigMethod(envVars, nodeEngineConfig);
1841
+ }
1842
+ }
1843
+ }
1844
+ return nodeEngineConfig;
1845
+ }
1846
+ /**
1847
+ * Handles the initialisation of the extensions when the engine has been constructed.
1848
+ * @param envVars The environment variables for the node.
1849
+ * @param engineCore The engine core instance.
1850
+ * @returns Nothing.
1851
+ */
1852
+ async function extensionsInitialiseEngine(envVars, engineCore) {
1853
+ if (core.Is.stringValue(envVars.extensions)) {
1854
+ const extensions = envVars.extensions.split(",");
1855
+ for (const extension of extensions) {
1856
+ let initialiseEngineMethod;
1857
+ try {
1858
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionInitialisingEngine", { extension }));
1859
+ initialiseEngineMethod =
1860
+ await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngine");
1861
+ }
1862
+ catch { }
1863
+ if (core.Is.function(initialiseEngineMethod)) {
1864
+ await initialiseEngineMethod(engineCore);
1865
+ }
1866
+ }
1867
+ }
1868
+ }
1869
+ /**
1870
+ * Handles the initialisation of the extensions when the engine server has been constructed.
1871
+ * @param envVars The environment variables for the node.
1872
+ * @param engineCore The engine core instance.
1873
+ * @param engineServer The engine server instance.
1874
+ * @returns Nothing.
1875
+ */
1876
+ async function extensionsInitialiseEngineServer(envVars, engineCore, engineServer) {
1877
+ if (core.Is.stringValue(envVars.extensions)) {
1878
+ const extensions = envVars.extensions.split(",");
1879
+ for (const extension of extensions) {
1880
+ let initialiseEngineServerMethod;
1881
+ try {
1882
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionInitialisingEngineServer", { extension }));
1883
+ initialiseEngineServerMethod =
1884
+ await modules.ModuleHelper.getModuleMethod(extension, "extensionInitialiseEngineServer");
1885
+ }
1886
+ catch { }
1887
+ if (core.Is.function(initialiseEngineServerMethod)) {
1888
+ await initialiseEngineServerMethod(engineCore, engineServer);
1889
+ }
1890
+ }
1891
+ }
1892
+ }
1893
+ /**
1894
+ * Handles the shutdown of the extensions.
1895
+ * @param envVars The environment variables for the node.
1896
+ * @param engineCore The engine core instance.
1897
+ * @returns Nothing.
1898
+ */
1899
+ async function shutdownExtensions(envVars, engineCore) {
1900
+ if (core.Is.stringValue(envVars.extensions)) {
1901
+ const extensions = envVars.extensions.split(",");
1902
+ for (const extension of extensions) {
1903
+ let shutdownMethod;
1904
+ try {
1905
+ engineCore.logInfo(core.I18n.formatMessage("node.extensionShutdown", { extension }));
1906
+ shutdownMethod = await modules.ModuleHelper.getModuleMethod(extension, "extensionShutdown");
1907
+ }
1908
+ catch { }
1909
+ if (core.Is.function(shutdownMethod)) {
1910
+ await shutdownMethod();
1911
+ }
1912
+ }
1913
+ }
1914
+ }
1915
+
1916
+ // Copyright 2024 IOTA Stiftung.
1917
+ // SPDX-License-Identifier: Apache-2.0.
1527
1918
  /**
1528
1919
  * Start the engine server.
1529
1920
  * @param nodeOptions Optional run options for the engine server.
1530
- * @param engineServerConfig The configuration for the engine server.
1921
+ * @param nodeEngineConfig The configuration for the engine server.
1531
1922
  * @param envVars The environment variables.
1532
1923
  * @returns The engine server.
1533
1924
  */
1534
- async function start(nodeOptions, engineServerConfig, envVars) {
1535
- envVars.storageFileRoot ??= "";
1925
+ async function start(nodeOptions, nodeEngineConfig, envVars) {
1536
1926
  const entityStorageConnectorType = envVars.entityStorageConnectorType?.split(",") ?? [];
1537
1927
  const blobStorageConnectorType = envVars.blobStorageConnectorType?.split(",") ?? [];
1928
+ // If the blob storage or entity storage is configured with file connectors
1929
+ // then we need to make sure the storageFileRoot is set
1538
1930
  if ((entityStorageConnectorType.includes(engineTypes.EntityStorageConnectorType.File) ||
1539
1931
  blobStorageConnectorType.includes(engineTypes.BlobStorageConnectorType.File) ||
1540
1932
  core.Is.empty(nodeOptions?.stateStorage)) &&
@@ -1543,24 +1935,26 @@ async function start(nodeOptions, engineServerConfig, envVars) {
1543
1935
  storageFileRoot: `${nodeOptions?.envPrefix ?? ""}_STORAGE_FILE_ROOT`
1544
1936
  });
1545
1937
  }
1546
- // Create the engine instance using file state storage and custom bootstrap.
1938
+ // Create the engine instance using file state storage unless one is configured in options
1547
1939
  const engine$1 = new engine.Engine({
1548
- config: engineServerConfig,
1940
+ config: nodeEngineConfig,
1549
1941
  stateStorage: nodeOptions?.stateStorage ?? new engineCore.FileStateStorage(envVars.stateFilename ?? ""),
1550
1942
  customBootstrap: async (core, engineContext) => bootstrap(core, engineContext, envVars)
1551
1943
  });
1944
+ // Construct the server with the engine.
1945
+ const server = new engineServer.EngineServer({ engineCore: engine$1 });
1552
1946
  // Extend the engine.
1553
1947
  if (core.Is.function(nodeOptions?.extendEngine)) {
1554
- console.info("Extending Engine");
1948
+ engine$1.logInfo(core.I18n.formatMessage("node.extendingEngine"));
1555
1949
  await nodeOptions.extendEngine(engine$1);
1556
1950
  }
1557
- // Construct the server with the engine.
1558
- const server = new engineServer.EngineServer({ engineCore: engine$1 });
1951
+ await extensionsInitialiseEngine(envVars, engine$1);
1559
1952
  // Extend the engine server.
1560
1953
  if (core.Is.function(nodeOptions?.extendEngineServer)) {
1561
- console.info("Extending Engine Server");
1954
+ engine$1.logInfo(core.I18n.formatMessage("node.extendingEngineServer"));
1562
1955
  await nodeOptions?.extendEngineServer(server);
1563
1956
  }
1957
+ await extensionsInitialiseEngineServer(envVars, engine$1, server);
1564
1958
  // Need to register the engine with the factory so that background tasks
1565
1959
  // can clone it to spawn new instances.
1566
1960
  engineModels.EngineCoreFactory.register("engine", () => engine$1);
@@ -1569,14 +1963,17 @@ async function start(nodeOptions, engineServerConfig, envVars) {
1569
1963
  if (canContinue) {
1570
1964
  return {
1571
1965
  engine: engine$1,
1572
- server
1966
+ server,
1967
+ shutdown: async () => {
1968
+ await server.stop();
1969
+ await shutdownExtensions(envVars, engine$1);
1970
+ }
1573
1971
  };
1574
1972
  }
1575
1973
  }
1576
1974
 
1577
1975
  // Copyright 2024 IOTA Stiftung.
1578
1976
  // SPDX-License-Identifier: Apache-2.0.
1579
- /* eslint-disable no-console */
1580
1977
  /**
1581
1978
  * Run the TWIN Node server.
1582
1979
  * @param nodeOptions Optional configuration options for running the server.
@@ -1587,41 +1984,60 @@ async function run(nodeOptions) {
1587
1984
  nodeOptions ??= {};
1588
1985
  const serverInfo = {
1589
1986
  name: nodeOptions?.serverName ?? "TWIN Node Server",
1590
- version: nodeOptions?.serverVersion ?? "0.0.2-next.2" // x-release-please-version
1987
+ version: nodeOptions?.serverVersion ?? "0.0.2-next.21" // x-release-please-version
1591
1988
  };
1592
- console.log(`\u001B[4m🌩️ ${serverInfo.name} v${serverInfo.version}\u001B[24m\n`);
1989
+ cliCore.CLIDisplay.header(serverInfo.name, serverInfo.version, "🌩️ ");
1593
1990
  if (!core.Is.stringValue(nodeOptions?.executionDirectory)) {
1594
1991
  nodeOptions.executionDirectory = getExecutionDirectory();
1595
1992
  }
1596
- console.info("Execution Directory:", nodeOptions.executionDirectory);
1993
+ cliCore.CLIDisplay.value("Execution Directory", nodeOptions.executionDirectory);
1597
1994
  nodeOptions.localesDirectory =
1598
1995
  nodeOptions?.localesDirectory ??
1599
1996
  path.resolve(path.join(nodeOptions.executionDirectory, "dist", "locales"));
1600
- console.info("Locales Directory:", nodeOptions.localesDirectory);
1997
+ cliCore.CLIDisplay.value("Locales Directory", nodeOptions.localesDirectory);
1601
1998
  await initialiseLocales(nodeOptions.localesDirectory);
1602
1999
  if (core.Is.empty(nodeOptions?.openApiSpecFile)) {
1603
- const specFile = path.resolve(path.join(nodeOptions.executionDirectory, "docs", "open-api", "spec.json"));
1604
- console.info("Default OpenAPI Spec File:", specFile);
2000
+ const specFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "docs", "open-api", "spec.json"));
2001
+ cliCore.CLIDisplay.value("Default OpenAPI Spec File", specFile);
1605
2002
  if (await fileExists(specFile)) {
1606
2003
  nodeOptions ??= {};
1607
2004
  nodeOptions.openApiSpecFile = specFile;
1608
2005
  }
1609
2006
  }
2007
+ else {
2008
+ cliCore.CLIDisplay.value("OpenAPI Spec File", nodeOptions.openApiSpecFile);
2009
+ }
2010
+ if (core.Is.empty(nodeOptions?.favIconFile)) {
2011
+ const favIconFile = path.resolve(path.join(nodeOptions.executionDirectory ?? "", "static", "favicon.png"));
2012
+ cliCore.CLIDisplay.value("Default Favicon File", favIconFile);
2013
+ if (await fileExists(favIconFile)) {
2014
+ nodeOptions ??= {};
2015
+ nodeOptions.favIconFile = favIconFile;
2016
+ }
2017
+ }
2018
+ else {
2019
+ cliCore.CLIDisplay.value("Favicon File", nodeOptions.favIconFile);
2020
+ }
1610
2021
  nodeOptions.envPrefix ??= "TWIN_NODE_";
1611
- console.info("Environment Prefix:", nodeOptions.envPrefix);
1612
- const { engineServerConfig, nodeEnvVars: envVars } = await buildConfiguration(process.env, nodeOptions, serverInfo);
1613
- console.info();
1614
- const startResult = await start(nodeOptions, engineServerConfig, envVars);
2022
+ cliCore.CLIDisplay.value("Environment Variable Prefix", nodeOptions.envPrefix);
2023
+ overrideModuleImport(nodeOptions.executionDirectory ?? "");
2024
+ const { nodeEngineConfig, nodeEnvVars: envVars } = await buildConfiguration(
2025
+ // This is the only location in the code base that should access process.env directly
2026
+ // eslint-disable-next-line no-restricted-syntax
2027
+ process.env, nodeOptions, serverInfo);
2028
+ cliCore.CLIDisplay.break();
2029
+ const startResult = await start(nodeOptions, nodeEngineConfig, envVars);
1615
2030
  if (!core.Is.empty(startResult)) {
1616
2031
  for (const signal of ["SIGHUP", "SIGINT", "SIGTERM"]) {
1617
2032
  process.on(signal, async () => {
1618
- await startResult.server.stop();
2033
+ cliCore.CLIDisplay.value("Terminate Signal", signal);
2034
+ await startResult.shutdown();
1619
2035
  });
1620
2036
  }
1621
2037
  }
1622
2038
  }
1623
2039
  catch (err) {
1624
- console.error(core.ErrorHelper.formatErrors(err).join("\n"));
2040
+ cliCore.CLIDisplay.error(err);
1625
2041
  // eslint-disable-next-line unicorn/no-process-exit
1626
2042
  process.exit(1);
1627
2043
  }
@@ -1638,14 +2054,15 @@ async function buildConfiguration(processEnv, options, serverInfo) {
1638
2054
  let defaultEnvOnly = false;
1639
2055
  if (core.Is.empty(options?.envFilenames)) {
1640
2056
  const envFile = path.resolve(path.join(options.executionDirectory ?? "", ".env"));
1641
- console.info("Default Environment File:", envFile);
2057
+ cliCore.CLIDisplay.value("Default Environment File", envFile);
1642
2058
  options ??= {};
1643
2059
  options.envFilenames = [envFile];
1644
2060
  defaultEnvOnly = true;
1645
2061
  }
1646
2062
  if (core.Is.arrayValue(options?.envFilenames)) {
1647
2063
  const output = dotenv__namespace.config({
1648
- path: options?.envFilenames
2064
+ path: options?.envFilenames,
2065
+ quiet: true
1649
2066
  });
1650
2067
  // We don't want to throw an error if the default environment file is not found.
1651
2068
  // Only if we have custom environment files.
@@ -1660,58 +2077,114 @@ async function buildConfiguration(processEnv, options, serverInfo) {
1660
2077
  // Expand any environment variables that use the @file: syntax
1661
2078
  const keys = Object.keys(envVars);
1662
2079
  for (const key of keys) {
1663
- if (core.Is.stringValue(envVars[key]) && envVars[key].startsWith("@file:")) {
2080
+ if (core.Is.stringValue(envVars[key]) &&
2081
+ (envVars[key].startsWith("@text:") || envVars[key].startsWith("@json:"))) {
1664
2082
  const filePath = envVars[key].slice(6).trim();
1665
2083
  const embeddedFile = path.resolve(path.join(options.executionDirectory ?? "", filePath));
1666
- console.info(`Expanding Environment Variable: ${key} from file: ${embeddedFile}`);
1667
- const fileContent = await loadJsonFile(embeddedFile);
1668
- envVars[key] = fileContent;
2084
+ if (envVars[key].startsWith("@text:")) {
2085
+ cliCore.CLIDisplay.value(`Expanding Environment Variable: ${key} from text file`, embeddedFile);
2086
+ envVars[key] = await loadTextFile(embeddedFile);
2087
+ }
2088
+ else if (envVars[key].startsWith("@json:")) {
2089
+ cliCore.CLIDisplay.value(`Expanding Environment Variable: ${key} from JSON file`, embeddedFile);
2090
+ envVars[key] = await loadJsonFile(embeddedFile);
2091
+ }
1669
2092
  }
1670
2093
  }
1671
2094
  // Extend the environment variables with any additional custom configuration.
1672
2095
  if (core.Is.function(options?.extendEnvVars)) {
1673
- console.info("Extending Environment Variables");
2096
+ cliCore.CLIDisplay.task("Extending Environment Variables");
1674
2097
  await options.extendEnvVars(envVars);
1675
2098
  }
1676
2099
  // Build the engine configuration from the environment variables.
1677
- const coreConfig = buildEngineConfiguration(envVars);
1678
- const engineServerConfig = buildEngineServerConfiguration(envVars, coreConfig, serverInfo, options?.openApiSpecFile);
2100
+ const coreConfig = await buildEngineConfiguration(envVars);
2101
+ const engineServerConfig = await buildEngineServerConfiguration(envVars, coreConfig, serverInfo, options?.openApiSpecFile, options?.favIconFile);
1679
2102
  // Merge any custom configuration provided in the options.
1680
2103
  if (core.Is.arrayValue(options?.configFilenames)) {
1681
2104
  for (const configFile of options.configFilenames) {
1682
- console.info("Loading Configuration File:", configFile);
2105
+ cliCore.CLIDisplay.value("Loading Configuration File", configFile);
1683
2106
  const configFilePath = path.resolve(path.join(options.executionDirectory ?? "", configFile));
1684
2107
  const config = await loadJsonFile(configFilePath);
1685
2108
  Object.assign(engineServerConfig, config);
1686
2109
  }
1687
2110
  }
1688
2111
  if (core.Is.objectValue(options?.config)) {
1689
- console.info("Merging Custom Configuration");
2112
+ cliCore.CLIDisplay.task("Merging Custom Configuration");
1690
2113
  Object.assign(engineServerConfig, options.config);
1691
2114
  }
1692
2115
  // Merge any custom configuration provided in the options.
1693
2116
  if (core.Is.function(options?.extendConfig)) {
1694
- console.info("Extending Configuration");
1695
- await options.extendConfig(engineServerConfig);
2117
+ cliCore.CLIDisplay.task("Extending Configuration");
2118
+ await options.extendConfig(envVars, engineServerConfig);
1696
2119
  }
1697
- return { engineServerConfig, nodeEnvVars: envVars };
2120
+ const nodeEngineConfig = await extensionsConfiguration(envVars, engineServerConfig);
2121
+ return { nodeEngineConfig, nodeEnvVars: envVars };
2122
+ }
2123
+ /**
2124
+ * Override module imports to use local files where possible.
2125
+ * @param executionDirectory The execution directory for resolving local module paths.
2126
+ */
2127
+ function overrideModuleImport(executionDirectory) {
2128
+ modules.ModuleHelper.overrideImport(async (moduleName) => {
2129
+ // If the module path for example when dynamically loading
2130
+ // modules looks like a local file then we try to resolve
2131
+ // using the local file system
2132
+ const isLocal = modules.ModuleHelper.isLocalModule(moduleName);
2133
+ if (isLocal) {
2134
+ // See if we can just resolve the filename locally
2135
+ let localFilename = path.resolve(moduleName);
2136
+ let exists = await fileExists(localFilename);
2137
+ if (!exists) {
2138
+ // Doesn't exist in the current directory, try the execution directory
2139
+ localFilename = path.resolve(executionDirectory, moduleName);
2140
+ exists = await fileExists(localFilename);
2141
+ }
2142
+ if (exists) {
2143
+ // If the module exists then we can load it, otherwise
2144
+ // we fallback to regular handling to see if that can import it
2145
+ return {
2146
+ module: await import(process.platform === "win32" ? `file://${localFilename}` : localFilename),
2147
+ useDefault: false
2148
+ };
2149
+ }
2150
+ }
2151
+ // The filename doesn't look like a local module, so just use default handling
2152
+ return {
2153
+ useDefault: true
2154
+ };
2155
+ });
1698
2156
  }
1699
2157
 
2158
+ exports.ATTESTATION_VERIFICATION_METHOD_ID = ATTESTATION_VERIFICATION_METHOD_ID;
2159
+ exports.AUTH_SIGNING_KEY_ID = AUTH_SIGNING_KEY_ID;
2160
+ exports.BLOB_STORAGE_ENCRYPTION_KEY_ID = BLOB_STORAGE_ENCRYPTION_KEY_ID;
2161
+ exports.IMMUTABLE_PROOF_VERIFICATION_METHOD_ID = IMMUTABLE_PROOF_VERIFICATION_METHOD_ID;
1700
2162
  exports.NodeFeatures = NodeFeatures;
2163
+ exports.SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID = SYNCHRONISED_STORAGE_BLOB_STORAGE_ENCRYPTION_KEY_ID;
2164
+ exports.VC_AUTHENTICATION_VERIFICATION_METHOD_ID = VC_AUTHENTICATION_VERIFICATION_METHOD_ID;
1701
2165
  exports.bootstrap = bootstrap;
1702
- exports.bootstrapAttestationMethod = bootstrapAttestationMethod;
1703
2166
  exports.bootstrapAuth = bootstrapAuth;
1704
2167
  exports.bootstrapBlobEncryption = bootstrapBlobEncryption;
1705
2168
  exports.bootstrapImmutableProofMethod = bootstrapImmutableProofMethod;
1706
2169
  exports.bootstrapNodeIdentity = bootstrapNodeIdentity;
1707
2170
  exports.bootstrapNodeUser = bootstrapNodeUser;
2171
+ exports.bootstrapSynchronisedStorage = bootstrapSynchronisedStorage;
1708
2172
  exports.buildConfiguration = buildConfiguration;
1709
2173
  exports.buildEngineConfiguration = buildEngineConfiguration;
1710
2174
  exports.buildEngineServerConfiguration = buildEngineServerConfiguration;
2175
+ exports.directoryExists = directoryExists;
2176
+ exports.extensionsConfiguration = extensionsConfiguration;
2177
+ exports.extensionsInitialiseEngine = extensionsInitialiseEngine;
2178
+ exports.extensionsInitialiseEngineServer = extensionsInitialiseEngineServer;
1711
2179
  exports.fileExists = fileExists;
1712
2180
  exports.getExecutionDirectory = getExecutionDirectory;
1713
2181
  exports.getFeatures = getFeatures;
2182
+ exports.getFiles = getFiles;
2183
+ exports.getSubFolders = getSubFolders;
1714
2184
  exports.initialiseLocales = initialiseLocales;
1715
2185
  exports.loadJsonFile = loadJsonFile;
2186
+ exports.loadTextFile = loadTextFile;
2187
+ exports.overrideModuleImport = overrideModuleImport;
1716
2188
  exports.run = run;
2189
+ exports.shutdownExtensions = shutdownExtensions;
1717
2190
  exports.start = start;