@harperfast/harper 5.2.13 → 5.3.0-beta.1

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 (655) hide show
  1. package/agent/mcpTools.ts +1 -1
  2. package/agent/session.ts +25 -14
  3. package/bin/cliOperations.ts +58 -9
  4. package/bin/copyDb.ts +282 -59
  5. package/bin/deploySetup.ts +16 -5
  6. package/bin/harper.ts +1 -1
  7. package/bin/help.ts +4 -1
  8. package/bin/lite.ts +4 -1
  9. package/bin/restart.ts +120 -4
  10. package/bin/run.ts +6 -11
  11. package/bin/upgrade.js +7 -3
  12. package/bin/workloadIdentity.ts +119 -0
  13. package/components/Application.ts +3436 -242
  14. package/components/ApplicationScope.ts +8 -0
  15. package/components/EntryHandler.ts +59 -39
  16. package/components/OptionsWatcher.ts +440 -98
  17. package/components/RuntimeModuleTracker.ts +38 -7
  18. package/components/Scope.ts +56 -15
  19. package/components/awaitRestart.ts +84 -0
  20. package/components/componentLoader.ts +381 -32
  21. package/components/componentPreparationLock.ts +16 -5
  22. package/components/deploymentOperations.ts +4 -1
  23. package/components/deploymentRecorder.ts +9 -2
  24. package/components/mcp/adapters/harperHttp.ts +4 -0
  25. package/components/mcp/listChanged.ts +4 -0
  26. package/components/mcp/toolRegistry.ts +2 -0
  27. package/components/mcp/tools/operations.ts +9 -0
  28. package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
  29. package/components/operations.js +537 -113
  30. package/components/operationsValidation.js +98 -3
  31. package/components/packageComponent.ts +25 -1
  32. package/components/requestRestart.ts +11 -0
  33. package/components/status/ComponentStatusRegistry.ts +59 -0
  34. package/config/RootConfigWatcher.ts +240 -40
  35. package/config/configReadRetry.ts +62 -0
  36. package/config/configUtils.ts +357 -48
  37. package/config/harperConfigEnvVars.ts +170 -27
  38. package/config/parseConfigFile.ts +34 -0
  39. package/config/readConfigFileSync.ts +44 -0
  40. package/config/watcherArming.ts +59 -0
  41. package/config-root.schema.json +33 -0
  42. package/dataLayer/blobBackup.ts +160 -50
  43. package/dataLayer/delete.ts +6 -1
  44. package/dataLayer/harperBridge/ResourceBridge.ts +80 -10
  45. package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  46. package/dataLayer/hdbInfoController.ts +34 -1
  47. package/dataLayer/insert.ts +44 -1
  48. package/dataLayer/rocksdbBackup.ts +53 -10
  49. package/dataLayer/schema.ts +11 -1
  50. package/dataLayer/schemaDescribe.ts +8 -1
  51. package/dist/agent/mcpTools.js +1 -1
  52. package/dist/agent/mcpTools.js.map +1 -1
  53. package/dist/agent/session.d.ts +22 -0
  54. package/dist/agent/session.js +26 -15
  55. package/dist/agent/session.js.map +1 -1
  56. package/dist/bin/cliOperations.js +61 -9
  57. package/dist/bin/cliOperations.js.map +1 -1
  58. package/dist/bin/copyDb.d.ts +12 -1
  59. package/dist/bin/copyDb.js +248 -60
  60. package/dist/bin/copyDb.js.map +1 -1
  61. package/dist/bin/deploySetup.d.ts +2 -0
  62. package/dist/bin/deploySetup.js +11 -3
  63. package/dist/bin/deploySetup.js.map +1 -1
  64. package/dist/bin/harper.js +1 -1
  65. package/dist/bin/harper.js.map +1 -1
  66. package/dist/bin/help.js +4 -1
  67. package/dist/bin/help.js.map +1 -1
  68. package/dist/bin/lite.js +4 -1
  69. package/dist/bin/lite.js.map +1 -1
  70. package/dist/bin/restart.js +91 -6
  71. package/dist/bin/restart.js.map +1 -1
  72. package/dist/bin/run.js +4 -10
  73. package/dist/bin/run.js.map +1 -1
  74. package/dist/bin/upgrade.js +4 -3
  75. package/dist/bin/upgrade.js.map +1 -1
  76. package/dist/bin/workloadIdentity.d.ts +18 -0
  77. package/dist/bin/workloadIdentity.js +100 -0
  78. package/dist/bin/workloadIdentity.js.map +1 -0
  79. package/dist/components/Application.d.ts +235 -17
  80. package/dist/components/Application.js +3038 -238
  81. package/dist/components/Application.js.map +1 -1
  82. package/dist/components/ApplicationScope.d.ts +8 -0
  83. package/dist/components/ApplicationScope.js +7 -0
  84. package/dist/components/ApplicationScope.js.map +1 -1
  85. package/dist/components/EntryHandler.js +26 -10
  86. package/dist/components/EntryHandler.js.map +1 -1
  87. package/dist/components/OptionsWatcher.d.ts +4 -0
  88. package/dist/components/OptionsWatcher.js +440 -99
  89. package/dist/components/OptionsWatcher.js.map +1 -1
  90. package/dist/components/RuntimeModuleTracker.js +40 -6
  91. package/dist/components/RuntimeModuleTracker.js.map +1 -1
  92. package/dist/components/Scope.js +52 -13
  93. package/dist/components/Scope.js.map +1 -1
  94. package/dist/components/awaitRestart.d.ts +33 -0
  95. package/dist/components/awaitRestart.js +61 -0
  96. package/dist/components/awaitRestart.js.map +1 -0
  97. package/dist/components/componentLoader.d.ts +38 -1
  98. package/dist/components/componentLoader.js +313 -24
  99. package/dist/components/componentLoader.js.map +1 -1
  100. package/dist/components/componentPreparationLock.d.ts +5 -0
  101. package/dist/components/componentPreparationLock.js +14 -6
  102. package/dist/components/componentPreparationLock.js.map +1 -1
  103. package/dist/components/deploymentOperations.js +4 -1
  104. package/dist/components/deploymentOperations.js.map +1 -1
  105. package/dist/components/deploymentRecorder.d.ts +4 -2
  106. package/dist/components/deploymentRecorder.js +1 -0
  107. package/dist/components/deploymentRecorder.js.map +1 -1
  108. package/dist/components/mcp/adapters/harperHttp.js +4 -0
  109. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  110. package/dist/components/mcp/listChanged.js +5 -0
  111. package/dist/components/mcp/listChanged.js.map +1 -1
  112. package/dist/components/mcp/toolRegistry.d.ts +1 -0
  113. package/dist/components/mcp/toolRegistry.js.map +1 -1
  114. package/dist/components/mcp/tools/operations.d.ts +5 -0
  115. package/dist/components/mcp/tools/operations.js +9 -0
  116. package/dist/components/mcp/tools/operations.js.map +1 -1
  117. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
  118. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  119. package/dist/components/operations.d.ts +28 -0
  120. package/dist/components/operations.js +476 -113
  121. package/dist/components/operations.js.map +1 -1
  122. package/dist/components/operationsValidation.js +97 -3
  123. package/dist/components/operationsValidation.js.map +1 -1
  124. package/dist/components/packageComponent.js +24 -0
  125. package/dist/components/packageComponent.js.map +1 -1
  126. package/dist/components/requestRestart.d.ts +1 -0
  127. package/dist/components/requestRestart.js +7 -0
  128. package/dist/components/requestRestart.js.map +1 -1
  129. package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
  130. package/dist/components/status/ComponentStatusRegistry.js +63 -0
  131. package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
  132. package/dist/config/RootConfigWatcher.d.ts +9 -1
  133. package/dist/config/RootConfigWatcher.js +228 -37
  134. package/dist/config/RootConfigWatcher.js.map +1 -1
  135. package/dist/config/configReadRetry.d.ts +8 -0
  136. package/dist/config/configReadRetry.js +62 -0
  137. package/dist/config/configReadRetry.js.map +1 -0
  138. package/dist/config/configUtils.d.ts +11 -2
  139. package/dist/config/configUtils.js +304 -47
  140. package/dist/config/configUtils.js.map +1 -1
  141. package/dist/config/harperConfigEnvVars.d.ts +16 -0
  142. package/dist/config/harperConfigEnvVars.js +162 -25
  143. package/dist/config/harperConfigEnvVars.js.map +1 -1
  144. package/dist/config/parseConfigFile.d.ts +4 -0
  145. package/dist/config/parseConfigFile.js +35 -0
  146. package/dist/config/parseConfigFile.js.map +1 -0
  147. package/dist/config/readConfigFileSync.d.ts +1 -0
  148. package/dist/config/readConfigFileSync.js +47 -0
  149. package/dist/config/readConfigFileSync.js.map +1 -0
  150. package/dist/config/watcherArming.d.ts +15 -0
  151. package/dist/config/watcherArming.js +59 -0
  152. package/dist/config/watcherArming.js.map +1 -0
  153. package/dist/dataLayer/blobBackup.d.ts +49 -20
  154. package/dist/dataLayer/blobBackup.js +139 -50
  155. package/dist/dataLayer/blobBackup.js.map +1 -1
  156. package/dist/dataLayer/delete.js +1 -1
  157. package/dist/dataLayer/delete.js.map +1 -1
  158. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
  159. package/dist/dataLayer/harperBridge/ResourceBridge.js +72 -12
  160. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  161. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
  162. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  163. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
  164. package/dist/dataLayer/hdbInfoController.d.ts +10 -0
  165. package/dist/dataLayer/hdbInfoController.js +30 -1
  166. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  167. package/dist/dataLayer/insert.d.ts +9 -1
  168. package/dist/dataLayer/insert.js +30 -0
  169. package/dist/dataLayer/insert.js.map +1 -1
  170. package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
  171. package/dist/dataLayer/rocksdbBackup.js +45 -8
  172. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  173. package/dist/dataLayer/schema.js +8 -0
  174. package/dist/dataLayer/schema.js.map +1 -1
  175. package/dist/dataLayer/schemaDescribe.js +8 -1
  176. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  177. package/dist/index.d.ts +2 -1
  178. package/dist/index.js +4 -1
  179. package/dist/index.js.map +1 -1
  180. package/dist/json/systemSchema.json +55 -0
  181. package/dist/resources/DatabaseTransaction.d.ts +77 -0
  182. package/dist/resources/DatabaseTransaction.js +586 -54
  183. package/dist/resources/DatabaseTransaction.js.map +1 -1
  184. package/dist/resources/LMDBTransaction.d.ts +2 -1
  185. package/dist/resources/LMDBTransaction.js +43 -6
  186. package/dist/resources/LMDBTransaction.js.map +1 -1
  187. package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
  188. package/dist/resources/PrimaryRocksDatabase.js +49 -5
  189. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  190. package/dist/resources/RecordEncoder.d.ts +20 -0
  191. package/dist/resources/RecordEncoder.js +110 -9
  192. package/dist/resources/RecordEncoder.js.map +1 -1
  193. package/dist/resources/RequestTarget.d.ts +2 -0
  194. package/dist/resources/RequestTarget.js.map +1 -1
  195. package/dist/resources/Resource.js +117 -24
  196. package/dist/resources/Resource.js.map +1 -1
  197. package/dist/resources/ResourceInterface.d.ts +28 -1
  198. package/dist/resources/ResourceInterface.js.map +1 -1
  199. package/dist/resources/RocksIndexStore.d.ts +6 -1
  200. package/dist/resources/RocksIndexStore.js +24 -9
  201. package/dist/resources/RocksIndexStore.js.map +1 -1
  202. package/dist/resources/RocksTransactionLogStore.d.ts +24 -1
  203. package/dist/resources/RocksTransactionLogStore.js +160 -49
  204. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  205. package/dist/resources/Table.d.ts +165 -10
  206. package/dist/resources/Table.js +1805 -303
  207. package/dist/resources/Table.js.map +1 -1
  208. package/dist/resources/analytics/write.js +10 -3
  209. package/dist/resources/analytics/write.js.map +1 -1
  210. package/dist/resources/auditStore.d.ts +181 -2
  211. package/dist/resources/auditStore.js +640 -29
  212. package/dist/resources/auditStore.js.map +1 -1
  213. package/dist/resources/blob.d.ts +129 -9
  214. package/dist/resources/blob.js +992 -114
  215. package/dist/resources/blob.js.map +1 -1
  216. package/dist/resources/branchDatabase.d.ts +48 -0
  217. package/dist/resources/branchDatabase.js +892 -0
  218. package/dist/resources/branchDatabase.js.map +1 -0
  219. package/dist/resources/crdt.js +50 -12
  220. package/dist/resources/crdt.js.map +1 -1
  221. package/dist/resources/dataLoader.js +3 -4
  222. package/dist/resources/dataLoader.js.map +1 -1
  223. package/dist/resources/databases.d.ts +157 -14
  224. package/dist/resources/databases.js +1462 -325
  225. package/dist/resources/databases.js.map +1 -1
  226. package/dist/resources/defineTable.d.ts +10 -2
  227. package/dist/resources/defineTable.js +9 -1
  228. package/dist/resources/defineTable.js.map +1 -1
  229. package/dist/resources/derivedIndexRegistry.d.ts +5 -0
  230. package/dist/resources/derivedIndexRegistry.js +68 -0
  231. package/dist/resources/derivedIndexRegistry.js.map +1 -0
  232. package/dist/resources/derivedIndexRuntime.d.ts +215 -0
  233. package/dist/resources/derivedIndexRuntime.js +2027 -0
  234. package/dist/resources/derivedIndexRuntime.js.map +1 -0
  235. package/dist/resources/graphql.d.ts +1 -1
  236. package/dist/resources/graphql.js +52 -16
  237. package/dist/resources/graphql.js.map +1 -1
  238. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +103 -9
  239. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +854 -41
  240. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  241. package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
  242. package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
  243. package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
  244. package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
  245. package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
  246. package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
  247. package/dist/resources/longLivedTransactions.d.ts +71 -0
  248. package/dist/resources/longLivedTransactions.js +358 -0
  249. package/dist/resources/longLivedTransactions.js.map +1 -0
  250. package/dist/resources/models/backendRegistry.d.ts +26 -0
  251. package/dist/resources/models/backendRegistry.js +60 -2
  252. package/dist/resources/models/backendRegistry.js.map +1 -1
  253. package/dist/resources/models/bootstrap.d.ts +33 -1
  254. package/dist/resources/models/bootstrap.js +416 -31
  255. package/dist/resources/models/bootstrap.js.map +1 -1
  256. package/dist/resources/nodeIdMapping.d.ts +5 -0
  257. package/dist/resources/nodeIdMapping.js +49 -0
  258. package/dist/resources/nodeIdMapping.js.map +1 -1
  259. package/dist/resources/recordLock.d.ts +123 -0
  260. package/dist/resources/recordLock.js +315 -0
  261. package/dist/resources/recordLock.js.map +1 -0
  262. package/dist/resources/recordLockCoordinator.d.ts +557 -0
  263. package/dist/resources/recordLockCoordinator.js +2565 -0
  264. package/dist/resources/recordLockCoordinator.js.map +1 -0
  265. package/dist/resources/replayLogs.d.ts +14 -1
  266. package/dist/resources/replayLogs.js +157 -24
  267. package/dist/resources/replayLogs.js.map +1 -1
  268. package/dist/resources/replayLogsGuards.d.ts +94 -7
  269. package/dist/resources/replayLogsGuards.js +111 -7
  270. package/dist/resources/replayLogsGuards.js.map +1 -1
  271. package/dist/resources/replicatedApplyFailure.d.ts +16 -0
  272. package/dist/resources/replicatedApplyFailure.js +63 -0
  273. package/dist/resources/replicatedApplyFailure.js.map +1 -0
  274. package/dist/resources/scheduler/scheduler.js +3 -3
  275. package/dist/resources/scheduler/scheduler.js.map +1 -1
  276. package/dist/resources/search.d.ts +10 -4
  277. package/dist/resources/search.js +283 -37
  278. package/dist/resources/search.js.map +1 -1
  279. package/dist/resources/tracked.d.ts +5 -1
  280. package/dist/resources/tracked.js +74 -23
  281. package/dist/resources/tracked.js.map +1 -1
  282. package/dist/resources/transactionBroadcast.d.ts +1 -1
  283. package/dist/resources/transactionBroadcast.js +2 -2
  284. package/dist/resources/transactionBroadcast.js.map +1 -1
  285. package/dist/security/auth.js +61 -30
  286. package/dist/security/auth.js.map +1 -1
  287. package/dist/security/authn/oidc/claims.d.ts +22 -0
  288. package/dist/security/authn/oidc/claims.js +71 -0
  289. package/dist/security/authn/oidc/claims.js.map +1 -0
  290. package/dist/security/authn/oidc/identityToken.d.ts +27 -0
  291. package/dist/security/authn/oidc/identityToken.js +111 -0
  292. package/dist/security/authn/oidc/identityToken.js.map +1 -0
  293. package/dist/security/authn/oidc/jwks.d.ts +25 -0
  294. package/dist/security/authn/oidc/jwks.js +261 -0
  295. package/dist/security/authn/oidc/jwks.js.map +1 -0
  296. package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
  297. package/dist/security/authn/oidc/providers/generic.js +34 -0
  298. package/dist/security/authn/oidc/providers/generic.js.map +1 -0
  299. package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
  300. package/dist/security/authn/oidc/providers/githubActions.js +129 -0
  301. package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
  302. package/dist/security/authn/oidc/providers/index.d.ts +37 -0
  303. package/dist/security/authn/oidc/providers/index.js +24 -0
  304. package/dist/security/authn/oidc/providers/index.js.map +1 -0
  305. package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
  306. package/dist/security/authn/oidc/tokenExchange.js +306 -0
  307. package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
  308. package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
  309. package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
  310. package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
  311. package/dist/security/authn/oidc/types.d.ts +38 -0
  312. package/dist/security/authn/oidc/types.js +6 -0
  313. package/dist/security/authn/oidc/types.js.map +1 -0
  314. package/dist/security/certificateVerification/index.js +40 -11
  315. package/dist/security/certificateVerification/index.js.map +1 -1
  316. package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
  317. package/dist/security/certificateVerification/trustedIssuers.js +79 -0
  318. package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
  319. package/dist/security/certificateVerification/types.d.ts +1 -0
  320. package/dist/security/credentialProvenance.d.ts +35 -0
  321. package/dist/security/credentialProvenance.js +51 -0
  322. package/dist/security/credentialProvenance.js.map +1 -0
  323. package/dist/security/credentialRejection.d.ts +4 -0
  324. package/dist/security/credentialRejection.js +24 -0
  325. package/dist/security/credentialRejection.js.map +1 -0
  326. package/dist/security/deferredAuthentication.d.ts +36 -0
  327. package/dist/security/deferredAuthentication.js +70 -0
  328. package/dist/security/deferredAuthentication.js.map +1 -0
  329. package/dist/security/impersonation.d.ts +21 -0
  330. package/dist/security/impersonation.js +108 -9
  331. package/dist/security/impersonation.js.map +1 -1
  332. package/dist/security/jsLoader.d.ts +6 -0
  333. package/dist/security/jsLoader.js +77 -15
  334. package/dist/security/jsLoader.js.map +1 -1
  335. package/dist/security/keys.js +301 -71
  336. package/dist/security/keys.js.map +1 -1
  337. package/dist/security/operationScope.d.ts +21 -0
  338. package/dist/security/operationScope.js +36 -0
  339. package/dist/security/operationScope.js.map +1 -0
  340. package/dist/security/permissionsTranslator.js +21 -0
  341. package/dist/security/permissionsTranslator.js.map +1 -1
  342. package/dist/security/tokenAuthentication.d.ts +19 -1
  343. package/dist/security/tokenAuthentication.js +191 -10
  344. package/dist/security/tokenAuthentication.js.map +1 -1
  345. package/dist/security/user.js +4 -3
  346. package/dist/security/user.js.map +1 -1
  347. package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
  348. package/dist/server/DurableSubscriptionsSession.js +67 -10
  349. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  350. package/dist/server/REST.js +106 -2
  351. package/dist/server/REST.js.map +1 -1
  352. package/dist/server/graphqlQuerying.js +4 -0
  353. package/dist/server/graphqlQuerying.js.map +1 -1
  354. package/dist/server/http.d.ts +16 -1
  355. package/dist/server/http.js +122 -17
  356. package/dist/server/http.js.map +1 -1
  357. package/dist/server/itc/serverHandlers.js +8 -1
  358. package/dist/server/itc/serverHandlers.js.map +1 -1
  359. package/dist/server/jobs/jobProcess.js +6 -2
  360. package/dist/server/jobs/jobProcess.js.map +1 -1
  361. package/dist/server/jobs/jobs.js +4 -1
  362. package/dist/server/jobs/jobs.js.map +1 -1
  363. package/dist/server/liveSubscriptionAuth.d.ts +26 -4
  364. package/dist/server/liveSubscriptionAuth.js +105 -39
  365. package/dist/server/liveSubscriptionAuth.js.map +1 -1
  366. package/dist/server/loadRootComponents.js +49 -10
  367. package/dist/server/loadRootComponents.js.map +1 -1
  368. package/dist/server/mqtt.d.ts +2 -0
  369. package/dist/server/mqtt.js +165 -30
  370. package/dist/server/mqtt.js.map +1 -1
  371. package/dist/server/nodeName.d.ts +2 -0
  372. package/dist/server/nodeName.js +107 -23
  373. package/dist/server/nodeName.js.map +1 -1
  374. package/dist/server/serverHelpers/Headers.d.ts +27 -0
  375. package/dist/server/serverHelpers/Headers.js +145 -1
  376. package/dist/server/serverHelpers/Headers.js.map +1 -1
  377. package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
  378. package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
  379. package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
  380. package/dist/server/serverHelpers/Request.d.ts +5 -10
  381. package/dist/server/serverHelpers/Request.js +38 -136
  382. package/dist/server/serverHelpers/Request.js.map +1 -1
  383. package/dist/server/serverHelpers/contentTypes.d.ts +11 -0
  384. package/dist/server/serverHelpers/contentTypes.js +221 -40
  385. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  386. package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
  387. package/dist/server/serverHelpers/deployValidationState.js +9 -19
  388. package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
  389. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
  390. package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
  391. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
  392. package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
  393. package/dist/server/serverHelpers/registeredOperations.js +74 -21
  394. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  395. package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
  396. package/dist/server/serverHelpers/requestSanitization.js +20 -0
  397. package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
  398. package/dist/server/serverHelpers/serverHandlers.js +6 -3
  399. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  400. package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
  401. package/dist/server/serverHelpers/serverUtilities.js +178 -29
  402. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  403. package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
  404. package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
  405. package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
  406. package/dist/server/serverHelpers/uwsServer.js +19 -1
  407. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  408. package/dist/server/static.js +24 -28
  409. package/dist/server/static.js.map +1 -1
  410. package/dist/server/storageReclamation.d.ts +5 -0
  411. package/dist/server/storageReclamation.js +17 -1
  412. package/dist/server/storageReclamation.js.map +1 -1
  413. package/dist/server/threads/isolatedApplications.d.ts +47 -0
  414. package/dist/server/threads/isolatedApplications.js +171 -0
  415. package/dist/server/threads/isolatedApplications.js.map +1 -0
  416. package/dist/server/threads/itc.d.ts +7 -2
  417. package/dist/server/threads/itc.js +5 -1
  418. package/dist/server/threads/itc.js.map +1 -1
  419. package/dist/server/threads/logRotationTransport.d.ts +1 -0
  420. package/dist/server/threads/logRotationTransport.js +33 -0
  421. package/dist/server/threads/logRotationTransport.js.map +1 -0
  422. package/dist/server/threads/manageThreads.d.ts +83 -3
  423. package/dist/server/threads/manageThreads.js +769 -75
  424. package/dist/server/threads/manageThreads.js.map +1 -1
  425. package/dist/server/threads/socketRouter.d.ts +1 -0
  426. package/dist/server/threads/socketRouter.js +277 -31
  427. package/dist/server/threads/socketRouter.js.map +1 -1
  428. package/dist/server/threads/threadHeapMemory.d.ts +2 -0
  429. package/dist/server/threads/threadHeapMemory.js +31 -0
  430. package/dist/server/threads/threadHeapMemory.js.map +1 -0
  431. package/dist/server/threads/threadServer.js +76 -22
  432. package/dist/server/threads/threadServer.js.map +1 -1
  433. package/dist/sqlEngine/config.d.ts +1 -3
  434. package/dist/sqlEngine/config.js +19 -16
  435. package/dist/sqlEngine/config.js.map +1 -1
  436. package/dist/sqlTranslator/index.d.ts +1 -1
  437. package/dist/sqlTranslator/index.js +30 -7
  438. package/dist/sqlTranslator/index.js.map +1 -1
  439. package/dist/upgrade/directives/5-3-0.d.ts +7 -0
  440. package/dist/upgrade/directives/5-3-0.js +148 -0
  441. package/dist/upgrade/directives/5-3-0.js.map +1 -0
  442. package/dist/upgrade/directives/directivesController.js +2 -1
  443. package/dist/upgrade/directives/directivesController.js.map +1 -1
  444. package/dist/utility/OperationFunctionCaller.js +2 -1
  445. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  446. package/dist/utility/common_utils.d.ts +16 -0
  447. package/dist/utility/common_utils.js +32 -6
  448. package/dist/utility/common_utils.js.map +1 -1
  449. package/dist/utility/componentNames.d.ts +8 -0
  450. package/dist/utility/componentNames.js +12 -1
  451. package/dist/utility/componentNames.js.map +1 -1
  452. package/dist/utility/environment/environmentManager.js +3 -6
  453. package/dist/utility/environment/environmentManager.js.map +1 -1
  454. package/dist/utility/environment/systemInformation.d.ts +1 -0
  455. package/dist/utility/environment/systemInformation.js +1 -0
  456. package/dist/utility/environment/systemInformation.js.map +1 -1
  457. package/dist/utility/errors/commonErrors.d.ts +2 -0
  458. package/dist/utility/errors/commonErrors.js +2 -0
  459. package/dist/utility/errors/commonErrors.js.map +1 -1
  460. package/dist/utility/errors/hdbError.d.ts +33 -0
  461. package/dist/utility/errors/hdbError.js +58 -1
  462. package/dist/utility/errors/hdbError.js.map +1 -1
  463. package/dist/utility/globalSchema.d.ts +18 -0
  464. package/dist/utility/hdbTerms.d.ts +18 -0
  465. package/dist/utility/hdbTerms.js +20 -2
  466. package/dist/utility/hdbTerms.js.map +1 -1
  467. package/dist/utility/logging/harper_logger.d.ts +2 -0
  468. package/dist/utility/logging/harper_logger.js +286 -30
  469. package/dist/utility/logging/harper_logger.js.map +1 -1
  470. package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
  471. package/dist/utility/logging/logGenerationCoordinator.js +184 -0
  472. package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
  473. package/dist/utility/logging/logRotation.d.ts +46 -0
  474. package/dist/utility/logging/logRotation.js +365 -0
  475. package/dist/utility/logging/logRotation.js.map +1 -0
  476. package/dist/utility/logging/logRotator.d.ts +1 -1
  477. package/dist/utility/logging/logRotator.js +192 -85
  478. package/dist/utility/logging/logRotator.js.map +1 -1
  479. package/dist/utility/nodeIdentity.d.ts +9 -0
  480. package/dist/utility/nodeIdentity.js +58 -0
  481. package/dist/utility/nodeIdentity.js.map +1 -0
  482. package/dist/utility/npmUtilities.js +11 -7
  483. package/dist/utility/npmUtilities.js.map +1 -1
  484. package/dist/utility/operationPermissions.d.ts +3 -1
  485. package/dist/utility/operationPermissions.js +16 -1
  486. package/dist/utility/operationPermissions.js.map +1 -1
  487. package/dist/utility/operation_authorization.d.ts +10 -7
  488. package/dist/utility/operation_authorization.js +212 -41
  489. package/dist/utility/operation_authorization.js.map +1 -1
  490. package/dist/utility/watchPath.d.ts +29 -0
  491. package/dist/utility/watchPath.js +68 -0
  492. package/dist/utility/watchPath.js.map +1 -0
  493. package/dist/utility/watcherFallback.d.ts +41 -0
  494. package/dist/utility/watcherFallback.js +153 -0
  495. package/dist/utility/watcherFallback.js.map +1 -1
  496. package/dist/validation/configValidator.d.ts +12 -0
  497. package/dist/validation/configValidator.js +205 -75
  498. package/dist/validation/configValidator.js.map +1 -1
  499. package/dist/validation/installValidator.js +12 -0
  500. package/dist/validation/installValidator.js.map +1 -1
  501. package/dist/validation/validationWrapper.d.ts +11 -0
  502. package/dist/validation/validationWrapper.js +16 -3
  503. package/dist/validation/validationWrapper.js.map +1 -1
  504. package/index.ts +8 -0
  505. package/json/systemSchema.json +55 -0
  506. package/npm-shrinkwrap.json +286 -194
  507. package/package.json +15 -7
  508. package/resources/DESIGN.md +219 -52
  509. package/resources/DatabaseTransaction.ts +657 -52
  510. package/resources/LMDBTransaction.ts +46 -6
  511. package/resources/PrimaryRocksDatabase.ts +46 -7
  512. package/resources/RecordEncoder.ts +124 -9
  513. package/resources/RequestTarget.ts +2 -0
  514. package/resources/Resource.ts +114 -22
  515. package/resources/ResourceInterface.ts +31 -0
  516. package/resources/RocksIndexStore.ts +30 -9
  517. package/resources/RocksTransactionLogStore.ts +188 -51
  518. package/resources/Table.ts +1960 -301
  519. package/resources/analytics/write.ts +10 -3
  520. package/resources/auditStore.ts +647 -32
  521. package/resources/blob.ts +1029 -110
  522. package/resources/branchDatabase.ts +941 -0
  523. package/resources/crdt.ts +76 -12
  524. package/resources/dataLoader.ts +3 -4
  525. package/resources/databases.ts +1584 -320
  526. package/resources/defineTable.ts +18 -2
  527. package/resources/derivedIndexRegistry.ts +56 -0
  528. package/resources/derivedIndexRuntime.ts +2292 -0
  529. package/resources/graphql.ts +72 -17
  530. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +911 -48
  531. package/resources/indexes/hnswDerivedIndex.ts +531 -0
  532. package/resources/indexes/hnswPlaneBinding.ts +174 -0
  533. package/resources/longLivedTransactions.ts +360 -0
  534. package/resources/models/backendRegistry.ts +84 -2
  535. package/resources/models/bootstrap.ts +473 -28
  536. package/resources/nodeIdMapping.ts +50 -0
  537. package/resources/recordLock.ts +419 -0
  538. package/resources/recordLockCoordinator.ts +3043 -0
  539. package/resources/replayLogs.ts +152 -26
  540. package/resources/replayLogsGuards.ts +171 -8
  541. package/resources/replicatedApplyFailure.ts +77 -0
  542. package/resources/scheduler/scheduler.ts +4 -4
  543. package/resources/search.ts +288 -46
  544. package/resources/tracked.ts +73 -22
  545. package/resources/transactionBroadcast.ts +3 -3
  546. package/security/auth.ts +68 -29
  547. package/security/authn/oidc/claims.ts +72 -0
  548. package/security/authn/oidc/identityToken.ts +129 -0
  549. package/security/authn/oidc/jwks.ts +260 -0
  550. package/security/authn/oidc/providers/generic.ts +40 -0
  551. package/security/authn/oidc/providers/githubActions.ts +137 -0
  552. package/security/authn/oidc/providers/index.ts +52 -0
  553. package/security/authn/oidc/tokenExchange.ts +300 -0
  554. package/security/authn/oidc/trustPolicyOperations.ts +343 -0
  555. package/security/authn/oidc/types.ts +41 -0
  556. package/security/certificateVerification/index.ts +54 -13
  557. package/security/certificateVerification/trustedIssuers.ts +76 -0
  558. package/security/certificateVerification/types.ts +1 -0
  559. package/security/credentialProvenance.ts +47 -0
  560. package/security/credentialRejection.ts +22 -0
  561. package/security/deferredAuthentication.ts +71 -0
  562. package/security/impersonation.ts +117 -12
  563. package/security/jsLoader.ts +83 -18
  564. package/security/keys.ts +298 -72
  565. package/security/operationScope.ts +33 -0
  566. package/security/permissionsTranslator.js +23 -0
  567. package/security/tokenAuthentication.ts +233 -12
  568. package/security/user.ts +4 -3
  569. package/server/DESIGN.md +194 -16
  570. package/server/DurableSubscriptionsSession.ts +71 -11
  571. package/server/REST.ts +115 -4
  572. package/server/graphqlQuerying.ts +4 -0
  573. package/server/http.ts +133 -20
  574. package/server/itc/serverHandlers.js +8 -1
  575. package/server/jobs/jobProcess.ts +8 -2
  576. package/server/jobs/jobs.ts +4 -1
  577. package/server/liveSubscriptionAuth.ts +129 -46
  578. package/server/loadRootComponents.js +50 -8
  579. package/server/mqtt.ts +179 -38
  580. package/server/nodeName.ts +103 -21
  581. package/server/serverHelpers/Headers.ts +135 -0
  582. package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
  583. package/server/serverHelpers/Request.ts +33 -131
  584. package/server/serverHelpers/contentTypes.ts +217 -36
  585. package/server/serverHelpers/deployValidationState.ts +24 -13
  586. package/server/serverHelpers/operationAuthorizationState.ts +34 -3
  587. package/server/serverHelpers/registeredOperations.ts +79 -22
  588. package/server/serverHelpers/requestSanitization.ts +15 -0
  589. package/server/serverHelpers/serverHandlers.js +6 -3
  590. package/server/serverHelpers/serverUtilities.ts +232 -40
  591. package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
  592. package/server/serverHelpers/uwsServer.ts +17 -2
  593. package/server/static.ts +23 -29
  594. package/server/storageReclamation.ts +15 -2
  595. package/server/threads/isolatedApplications.ts +157 -0
  596. package/server/threads/itc.js +11 -1
  597. package/server/threads/logRotationTransport.ts +40 -0
  598. package/server/threads/manageThreads.js +771 -66
  599. package/server/threads/socketRouter.ts +291 -30
  600. package/server/threads/threadHeapMemory.ts +26 -0
  601. package/server/threads/threadServer.js +73 -22
  602. package/sqlTranslator/index.ts +31 -8
  603. package/studio/web/assets/{Chat-4RrB5134.js → Chat-D3j-1yY1.js} +1 -1
  604. package/studio/web/assets/{FloatingChat-omlNMDcJ.js → FloatingChat-BxJGYcfB.js} +3 -3
  605. package/studio/web/assets/{apiToken-Bke3wvfZ.js → apiToken-CT55oWOe.js} +1 -1
  606. package/studio/web/assets/{applications-DvFDYJgK.js → applications-D9Ct9_vm.js} +1 -1
  607. package/studio/web/assets/{cssMode-C1vRa7zh.js → cssMode-DV8H7VwA.js} +1 -1
  608. package/studio/web/assets/{editor-3XRWEDWX.js → editor-uatc0unt.js} +1 -1
  609. package/studio/web/assets/{html-CTY5tdMr.js → html-Bm6D6paN.js} +1 -1
  610. package/studio/web/assets/{htmlMode-CG1vSD9t.js → htmlMode-CEn7tpLG.js} +1 -1
  611. package/studio/web/assets/{index-Dfpeofdu.js → index-BIXW6Pu4.js} +5 -5
  612. package/studio/web/assets/{index.lazy-B1VOIv-t.js → index.lazy-UI7L-Vrk.js} +1 -1
  613. package/studio/web/assets/{javascript-CJeJzGnI.js → javascript-CJ0G3AFZ.js} +1 -1
  614. package/studio/web/assets/{jsonMode-CYPBwM82.js → jsonMode-DQADAYEa.js} +1 -1
  615. package/studio/web/assets/{languageServices-IKH4GUHl.js → languageServices-CAQJXWcI.js} +1 -1
  616. package/studio/web/assets/{lspLanguageFeatures-BC8_gxKG.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
  617. package/studio/web/assets/{notifications-BfKBpYcq.js → notifications-BbxTU6Aw.js} +1 -1
  618. package/studio/web/assets/{notifications-DFbArTfC.js → notifications-Cvb3P1lB.js} +1 -1
  619. package/studio/web/assets/{profile-Q4-T6c-S.js → profile-Yyb7gsvL.js} +1 -1
  620. package/studio/web/assets/{regions-CtkV0xje.js → regions-OgjGHlU5.js} +1 -1
  621. package/studio/web/assets/{register-1ZZuMsiA.js → register-6qwNEOY3.js} +2 -2
  622. package/studio/web/assets/{setComponentFile-ZWLcWv5X.js → setComponentFile-BilDMtgB.js} +1 -1
  623. package/studio/web/assets/{setup-DWprJyJy.js → setup-J6qJ7OIU.js} +2 -2
  624. package/studio/web/assets/{status--aNm8isn.js → status-0RWGcfyD.js} +1 -1
  625. package/studio/web/assets/{toggleHighContrast-Cq_lt3XD.js → toggleHighContrast-BIn-vErT.js} +1 -1
  626. package/studio/web/assets/{tsMode-pjgytARx.js → tsMode-DgUXku4d.js} +1 -1
  627. package/studio/web/assets/{typescript-Cdg0mqUh.js → typescript-C9orXcsM.js} +1 -1
  628. package/studio/web/assets/{useEntityRestURL-RZhaY8Rn.js → useEntityRestURL-BEoXXbUB.js} +1 -1
  629. package/studio/web/assets/{workers-BgoXIqQe.js → workers-JVzSDmgx.js} +1 -1
  630. package/studio/web/assets/{xml-BwSeDMiP.js → xml-Cq-S8S4X.js} +1 -1
  631. package/studio/web/assets/{yaml-DotCUG5l.js → yaml-sfoRdh1M.js} +1 -1
  632. package/studio/web/index.html +1 -1
  633. package/upgrade/directives/5-3-0.ts +132 -0
  634. package/upgrade/directives/directivesController.ts +2 -1
  635. package/utility/OperationFunctionCaller.ts +2 -1
  636. package/utility/common_utils.ts +30 -5
  637. package/utility/componentNames.ts +12 -0
  638. package/utility/environment/environmentManager.ts +3 -7
  639. package/utility/environment/systemInformation.ts +7 -0
  640. package/utility/errors/commonErrors.ts +4 -0
  641. package/utility/errors/hdbError.ts +57 -0
  642. package/utility/hdbTerms.ts +19 -0
  643. package/utility/logging/harper_logger.ts +278 -26
  644. package/utility/logging/logGenerationCoordinator.ts +196 -0
  645. package/utility/logging/logRotation.ts +367 -0
  646. package/utility/logging/logRotator.ts +213 -81
  647. package/utility/nodeIdentity.ts +45 -0
  648. package/utility/npmUtilities.ts +12 -8
  649. package/utility/operationPermissions.ts +18 -1
  650. package/utility/operation_authorization.ts +231 -42
  651. package/utility/watchPath.ts +63 -0
  652. package/utility/watcherFallback.ts +148 -0
  653. package/validation/configValidator.ts +215 -75
  654. package/validation/installValidator.ts +15 -0
  655. package/validation/validationWrapper.ts +18 -4
@@ -1,19 +1,62 @@
1
1
  const { isMainThread } = require('worker_threads');
2
2
  const { getTables } = require('../resources/databases.ts');
3
- const { loadComponentDirectories, loadComponent } = require('../components/componentLoader.ts');
3
+ const { loadComponentDirectories, loadComponent, readyComponentModules } = require('../components/componentLoader.ts');
4
4
  const { resetResources } = require('../resources/Resources.ts');
5
5
  const configUtils = require('../config/configUtils.ts');
6
6
  const { dirname } = require('path');
7
7
  const { loadCertificates } = require('../security/keys.ts');
8
- const { installApplications } = require('../components/Application.ts');
8
+ const { installApplications, recoverInterruptedActivations } = require('../components/Application.ts');
9
9
  const { errorForLog } = require('../utility/logging/harper_logger.ts');
10
+ const { CONFIG_PARAMS } = require('../utility/hdbTerms.ts');
10
11
 
11
12
  let loadedComponents = new Map();
13
+
14
+ /**
15
+ * Map every component directory to the reason activation recovery could not run. Used only when the scan
16
+ * fails globally, where the safe answer is "none of these are known-good" rather than "all of these are".
17
+ */
18
+ async function failEveryComponentClosed(cause) {
19
+ const failures = new Map();
20
+ const reason = cause instanceof Error ? cause : new Error(`Activation recovery could not run: ${String(cause)}`);
21
+ try {
22
+ const { readdir } = require('node:fs/promises');
23
+ const componentsRoot = configUtils.getConfigPath(CONFIG_PARAMS.COMPONENTSROOT);
24
+ for (const entry of await readdir(componentsRoot, { withFileTypes: true })) {
25
+ if (!entry.name.startsWith('.') && (entry.isDirectory() || entry.isSymbolicLink())) {
26
+ failures.set(entry.name, reason);
27
+ }
28
+ }
29
+ } catch (error) {
30
+ console.error(errorForLog(error));
31
+ }
32
+ return failures;
33
+ }
12
34
  /**
13
35
  * This is main entry point for loading the main set of global server modules that power Harper.
14
36
  * @returns {Promise<void>}
15
37
  */
16
38
  async function loadRootComponents(isWorkerThread = false) {
39
+ // Interrupted activations are settled FIRST, before installApplications() — not merely before the
40
+ // component scan. installApplications() installs whatever the root config names, so a candidate left
41
+ // half-swapped by a crash has to be resolved before that runs, or boot reinstalls over it. Failures are
42
+ // per component and returned, not thrown: the loader fails those components closed and loads the rest.
43
+ // Undefined, not an empty Map: a worker skips this branch entirely, and handing the loader an empty map
44
+ // would assert "nothing is unreconciled" on a thread that never checked. The loader treats undefined as
45
+ // "no verdict from boot" instead.
46
+ let interruptedActivationFailures;
47
+ try {
48
+ if (isMainThread && !process.env.HARPER_SAFE_MODE) {
49
+ interruptedActivationFailures = await recoverInterruptedActivations(
50
+ configUtils.getConfigPath(CONFIG_PARAMS.COMPONENTSROOT)
51
+ );
52
+ }
53
+ } catch (error) {
54
+ // The scan itself failed, so WHICH components are unsettled is unknown — loading them all would
55
+ // defeat the fail-closed contract this pass exists for. Every component present is failed closed
56
+ // instead, and a later reload cycle retries once the cause is gone.
57
+ console.error(errorForLog(error));
58
+ interruptedActivationFailures = await failEveryComponentClosed(error);
59
+ }
17
60
  try {
18
61
  if (isMainThread && !process.env.HARPER_SAFE_MODE) await installApplications();
19
62
  } catch (error) {
@@ -33,13 +76,12 @@ async function loadRootComponents(isWorkerThread = false) {
33
76
  });
34
77
  if (!process.env.HARPER_SAFE_MODE) {
35
78
  // once the global plugins are loaded, we now load all the CF and run applications (and their components)
36
- await loadComponentDirectories(loadedComponents, resources);
37
- }
38
- let allReady = [];
39
- for (let [serverModule] of loadedComponents) {
40
- if (serverModule.ready) allReady.push(serverModule.ready());
79
+ const readyComponentPromises = new WeakMap();
80
+ await loadComponentDirectories(loadedComponents, resources, readyComponentPromises, interruptedActivationFailures);
81
+ await readyComponentModules(loadedComponents.keys(), readyComponentPromises);
82
+ return;
41
83
  }
42
- if (allReady.length > 0) await Promise.all(allReady);
84
+ await readyComponentModules(loadedComponents.keys());
43
85
  }
44
86
 
45
87
  module.exports.loadRootComponents = loadRootComponents;
package/server/mqtt.ts CHANGED
@@ -3,7 +3,13 @@
3
3
  import { parser as makeParser, generate } from 'mqtt-packet';
4
4
  import { getSession, DurableSubscriptionsSession } from './DurableSubscriptionsSession.ts';
5
5
  import { getSuperUser } from '../security/user.ts';
6
- import { serializeMessage, getDeserializer } from './serverHelpers/contentTypes.ts';
6
+ import { getDeserializer } from './serverHelpers/contentTypes.ts';
7
+ import {
8
+ getSharedMessageEncoding,
9
+ getSharedFrame,
10
+ setSharedFrame,
11
+ resolveSharedPayload,
12
+ } from './serverHelpers/sharedMessageEncoding.ts';
7
13
  import { recordAction, addAnalyticsListener, recordActionBinary } from '../resources/analytics/write.ts';
8
14
  import { server } from '../server/Server.ts';
9
15
  import { get } from '../utility/environment/environmentManager.ts';
@@ -12,6 +18,14 @@ import { loggerWithTag } from '../utility/logging/logger.ts';
12
18
  import { forComponent as loggerForComponent } from '../utility/logging/harper_logger.ts';
13
19
  import { EventEmitter } from 'events';
14
20
  import { verifyCertificate } from '../security/certificateVerification/index.ts';
21
+ import { registerShutdownDrain } from '../components/shutdownDrain.ts';
22
+ import {
23
+ assertNoDeferredCredentialRejection,
24
+ getDeferredCredentialRejection,
25
+ } from '../security/deferredAuthentication.ts';
26
+
27
+ /** RFC 6455 private-use close code Harper already maps HTTP 401 to (see server/REST.ts). */
28
+ const WEBSOCKET_UNAUTHORIZED_CLOSE_CODE = 3000;
15
29
  const authEventLog = loggerWithTag('auth-event');
16
30
  const mqttLog = loggerForComponent('mqtt');
17
31
 
@@ -40,6 +54,7 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
40
54
  };
41
55
  // a no-op error handler to prevent unhandled error events from being rethrown
42
56
  (server as any).mqtt.events.on('error', () => {});
57
+ registerShutdownDisconnect();
43
58
  }
44
59
  const mqttSettings = (server as any).mqtt;
45
60
  function emitEvent(type: string, ...args: any[]) {
@@ -60,13 +75,29 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
60
75
 
61
76
  emitEvent('connection', ws);
62
77
  mqttLog.debug?.('Received WebSocket connection for MQTT from', ws._socket.remoteAddress);
78
+ // Both WebSocket entry points invoke this listener synchronously with the HTTP chain still
79
+ // pending (server/http.ts), so authentication has not recorded a credential rejection yet.
80
+ // It settles on the same promise the session principal comes from, which onSocket awaits
81
+ // before it processes any packet — the handlers below still attach synchronously, so no
82
+ // frame that arrives in the meantime is dropped.
83
+ const authenticated = Promise.resolve(chainCompletion).then(() => {
84
+ assertNoDeferredCredentialRejection(request);
85
+ return request?.user;
86
+ });
87
+ authenticated.catch((error) => {
88
+ mqttLog.info?.('Closing MQTT WebSocket connection, authentication was rejected', error);
89
+ ws.close(
90
+ WEBSOCKET_UNAUTHORIZED_CLOSE_CODE,
91
+ getDeferredCredentialRejection(request)?.message ?? 'Unauthorized'
92
+ );
93
+ });
63
94
  const { onMessage, onClose } = onSocket(
64
95
  ws,
65
96
  (message) => {
66
97
  ws.send(message);
67
98
  },
68
99
  request,
69
- Promise.resolve(chainCompletion).then(() => request?.user),
100
+ authenticated,
70
101
  mqttSettings
71
102
  );
72
103
  ws.on('message', onMessage);
@@ -168,6 +199,50 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
168
199
  );
169
200
  }
170
201
  }
202
+ const SERVER_SHUTTING_DOWN = 0x8b; // MQTT v5 DISCONNECT reason code
203
+ const REASON_STRING_LIMIT = 256; // bytes
204
+ /** Fixed header, message id and reason code, plus the property identifier and its length prefix. */
205
+ const ACK_PACKET_OVERHEAD = 16;
206
+
207
+ /**
208
+ * Every live MQTT connection on this worker, so a shutdown can announce itself: a bare socket close
209
+ * is indistinguishable to the client from a network failure.
210
+ */
211
+ const liveConnections = new Set<{ protocolVersion: () => number; send: (data: any) => void; close: () => void }>();
212
+ let shutdownDisconnectRegistered = false;
213
+
214
+ /** v3.1.1 has no server-to-client DISCONNECT, so those connections are only closed. */
215
+ export function disconnectClientsForShutdown() {
216
+ if (liveConnections.size === 0) return;
217
+ const disconnectPacket = generate({ cmd: 'disconnect', reasonCode: SERVER_SHUTTING_DOWN } as any, {
218
+ protocolVersion: 5,
219
+ });
220
+ for (const connection of liveConnections) {
221
+ try {
222
+ if (connection.protocolVersion() >= 5) connection.send(disconnectPacket);
223
+ } catch (error) {
224
+ mqttLog.debug?.('Could not notify MQTT connection of shutdown', error);
225
+ } finally {
226
+ try {
227
+ connection.close();
228
+ } catch (error) {
229
+ mqttLog.debug?.('Could not close MQTT connection during shutdown', error);
230
+ }
231
+ }
232
+ }
233
+ liveConnections.clear();
234
+ }
235
+
236
+ function registerShutdownDisconnect() {
237
+ if (shutdownDisconnectRegistered) return;
238
+ shutdownDisconnectRegistered = true;
239
+ registerShutdownDrain({
240
+ // Notifying clients is bounded and synchronous; it must never extend the shutdown deadline.
241
+ hasWork: () => false,
242
+ drain: async () => disconnectClientsForShutdown(),
243
+ });
244
+ }
245
+
171
246
  let addingMetrics,
172
247
  numberOfConnections = 0;
173
248
  function onSocket(socket, send, request, user, mqttSettings) {
@@ -191,7 +266,17 @@ function onSocket(socket, send, request, user, mqttSettings) {
191
266
  }
192
267
  let disconnected;
193
268
  numberOfConnections++;
269
+ const connection = {
270
+ protocolVersion: () => mqttOptions.protocolVersion,
271
+ send,
272
+ close: () => (socket.close ? socket.close() : socket.end()),
273
+ };
274
+ liveConnections.add(connection);
194
275
  let session: DurableSubscriptionsSession;
276
+ // [MQTT-3.1.2-29]: a client that asks for no problem information must not be sent a reason
277
+ // string on a PUBACK/PUBREC, and [MQTT-3.1.2-24] caps what it will accept at all.
278
+ let sendProblemInformation = true;
279
+ let maximumPacketSize: number | undefined;
195
280
  const mqttOptions = { protocolVersion: 4 };
196
281
  const parser = makeParser({ protocolVersion: 5 });
197
282
  function onMessage(data) {
@@ -199,6 +284,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
199
284
  }
200
285
  function onClose() {
201
286
  numberOfConnections--;
287
+ liveConnections.delete(connection);
202
288
  if (!disconnected) {
203
289
  disconnected = true;
204
290
  session?.disconnect?.(false);
@@ -236,6 +322,10 @@ function onSocket(socket, send, request, user, mqttSettings) {
236
322
  switch (command) {
237
323
  case 'connect':
238
324
  mqttOptions.protocolVersion = packet.protocolVersion;
325
+ // mqtt-packet parses this byte into a boolean, and a client may also send the raw 0.
326
+ const requestProblemInformation = packet.properties?.requestProblemInformation;
327
+ sendProblemInformation = requestProblemInformation !== false && requestProblemInformation !== 0;
328
+ maximumPacketSize = packet.properties?.maximumPacketSize;
239
329
  if (packet.username) {
240
330
  try {
241
331
  user = await server.getUser(packet.username, packet.password.toString(), request);
@@ -260,7 +350,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
260
350
  }
261
351
  emitEvent('auth-failed', packet, socket, error);
262
352
  recordActionBinary(false, 'connection', 'mqtt', 'connect');
263
- return sendPacket({
353
+ return generateAndSendPacket({
264
354
  // Send a connection acknowledgment with indication of auth failure
265
355
  cmd: 'connack',
266
356
  reasonCode: 0x04, // bad username or password, v3.1.1
@@ -271,7 +361,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
271
361
  if (!user && mqttSettings.requireAuthentication) {
272
362
  emitEvent('auth-failed', packet, socket);
273
363
  recordActionBinary(false, 'connection', 'mqtt', 'connect');
274
- return sendPacket({
364
+ return generateAndSendPacket({
275
365
  // Send a connection acknowledgment with indication of auth failure
276
366
  cmd: 'connack',
277
367
  reasonCode: 0x04, // bad username or password, v3.1.1
@@ -307,7 +397,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
307
397
  mqttLog.error?.(error);
308
398
  emitEvent('auth-failed', packet, socket, error);
309
399
  recordActionBinary(false, 'connection', 'mqtt', 'connect');
310
- return sendPacket({
400
+ return generateAndSendPacket({
311
401
  // Send a connection acknowledgment with indication of auth failure
312
402
  cmd: 'connack',
313
403
  reasonCode: error.code || 0x05,
@@ -316,28 +406,55 @@ function onSocket(socket, send, request, user, mqttSettings) {
316
406
  }
317
407
  emitEvent('connected', session, socket);
318
408
  recordActionBinary(true, 'connection', 'mqtt', 'connect');
319
- sendPacket({
409
+ generateAndSendPacket({
320
410
  // Send a connection acknowledgment
321
411
  cmd: 'connack',
322
412
  sessionPresent: session.sessionWasPresent,
323
413
  reasonCode: 0,
324
414
  returnCode: 0, // success
325
415
  });
326
- const listener = async (topic, message, messageId, subscription) => {
416
+ const listener = async (topic, message, messageId, subscription, version) => {
327
417
  try {
328
418
  if (disconnected) throw new Error('Session disconnected while trying to send message to', topic);
329
419
  const slashIndex = topic.indexOf('/', 1);
330
420
  const generalTopic = slashIndex > 0 ? topic.slice(0, slashIndex) : topic;
331
- sendPacket(
332
- {
421
+ const qos = subscription.qos || 0;
422
+ // Every subscriber of a topic serializes the same message to the same bytes, so the
423
+ // payload is encoded once per (message, content type) and reused across all of them.
424
+ const encoding = getSharedMessageEncoding(message, request, version);
425
+ const encoded = encoding.payload;
426
+ // only pay for a microtask when the serialization is genuinely still pending
427
+ const payload =
428
+ typeof (encoded as any)?.then === 'function'
429
+ ? await resolveSharedPayload(encoding, message, request, version)
430
+ : (encoded as Buffer | string);
431
+ if (qos > 0) {
432
+ // mqtt-packet requires a numeric message identifier once qos is non-zero
433
+ const packetData: any = {
333
434
  cmd: 'publish',
334
435
  topic,
335
- payload: await serialize(message),
436
+ payload,
336
437
  messageId: messageId || Math.floor(Math.random() * 100000000),
337
- qos: subscription.qos,
338
- },
339
- generalTopic
340
- );
438
+ qos,
439
+ };
440
+ sendPacket(generate(packetData, mqttOptions), packetMethodName(packetData), generalTopic);
441
+ } else {
442
+ // A QoS 0 PUBLISH carries no message identifier, so the whole packet depends only on
443
+ // the payload, the topic, and the protocol version (v5 emits a properties field that
444
+ // v3.1.1 omits) — share it across every QoS 0 subscriber that matches on those.
445
+ // The frame also varies on dup/retain/properties, which are fixed below; anything
446
+ // that starts varying them per subscriber (RETAIN propagation, v5 subscription
447
+ // identifiers) has to join the key or subscribers will be served the wrong flags.
448
+ const protocolVersion = mqttOptions.protocolVersion;
449
+ let packet = getSharedFrame(encoding, protocolVersion, topic);
450
+ if (packet === undefined) {
451
+ packet = generate({ cmd: 'publish', topic, payload, qos: 0, dup: false, retain: false }, mqttOptions);
452
+ // only worth retaining once a second subscriber has shown up; caching a packet
453
+ // for a fan-out of one just pins a whole buffer nothing will read
454
+ if (encoding.hits > 0) setSharedFrame(encoding, protocolVersion, topic, packet);
455
+ }
456
+ sendPacket(packet, 'publish', generalTopic);
457
+ }
341
458
  // wait if there is back-pressure
342
459
  const rawSocket = socket._socket ?? socket;
343
460
  if (rawSocket.writableNeedDrain) {
@@ -351,7 +468,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
351
468
  return false;
352
469
  }
353
470
  };
354
- session.setListener(listener as any);
471
+ session.setListener(listener);
355
472
  if (session.sessionWasPresent) await session.resume();
356
473
  break;
357
474
  case 'subscribe':
@@ -383,7 +500,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
383
500
  granted.push(grantedQos);
384
501
  }
385
502
  await session.committed;
386
- sendPacket({
503
+ generateAndSendPacket({
387
504
  // Send a subscription acknowledgment
388
505
  cmd: 'suback',
389
506
  granted,
@@ -395,7 +512,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
395
512
  for (const subscription of packet.unsubscriptions) {
396
513
  granted.push(session.removeSubscription(subscription) ? 0 : 17);
397
514
  }
398
- sendPacket({
515
+ generateAndSendPacket({
399
516
  // Send a subscription acknowledgment
400
517
  cmd: 'unsuback',
401
518
  granted,
@@ -404,7 +521,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
404
521
  break;
405
522
  }
406
523
  case 'pubrel':
407
- sendPacket({
524
+ generateAndSendPacket({
408
525
  // Send a publish response
409
526
  cmd: 'pubcomp',
410
527
  messageId: packet.messageId,
@@ -426,20 +543,44 @@ function onSocket(socket, send, request, user, mqttSettings) {
426
543
  emitEvent('error', error, socket, packet, session);
427
544
  mqttLog.warn?.(error);
428
545
  if (packet.qos > 0) {
429
- sendPacket(
430
- {
431
- // Send a publish acknowledgment
432
- cmd: responseCmd,
433
- messageId: packet.messageId,
434
- reasonCode: 0x80, // unspecified error (only MQTT v5 supports error codes)
435
- },
436
- packet.topic
437
- );
546
+ // A publish to a topic no resource handles is the same miss addSubscription already
547
+ // answers with a specific code; reporting it as "unspecified error" leaves it
548
+ // indistinguishable from an internal failure.
549
+ const publishPacket: any = {
550
+ // Send a publish acknowledgment
551
+ cmd: responseCmd,
552
+ messageId: packet.messageId,
553
+ reasonCode:
554
+ mqttOptions.protocolVersion < 5
555
+ ? 0x80 // the only error code in v3.1.1
556
+ : error?.statusCode === 403
557
+ ? 0x87 // not authorized
558
+ : error?.statusCode === 404
559
+ ? 0x90 // topic name invalid
560
+ : 0x80, // unspecified error
561
+ };
562
+ // Only errors this layer maps to a code of their own are safe to describe: any other
563
+ // failure carries an internal message that a client must not be handed. Both limits
564
+ // are in encoded bytes, so measure and trim the string in bytes too.
565
+ const describable =
566
+ mqttOptions.protocolVersion >= 5 &&
567
+ sendProblemInformation &&
568
+ error?.message &&
569
+ (error.statusCode === 403 || error.statusCode === 404);
570
+ const reasonString = describable
571
+ ? Buffer.from(String(error.message), 'utf8').subarray(0, REASON_STRING_LIMIT).toString('utf8')
572
+ : undefined;
573
+ if (
574
+ reasonString &&
575
+ (!maximumPacketSize || maximumPacketSize >= Buffer.byteLength(reasonString) + ACK_PACKET_OVERHEAD)
576
+ )
577
+ publishPacket.properties = { reasonString };
578
+ generateAndSendPacket(publishPacket, packet.topic);
438
579
  }
439
580
  break;
440
581
  }
441
582
  if (packet.qos > 0) {
442
- sendPacket(
583
+ generateAndSendPacket(
443
584
  {
444
585
  // Send a publish acknowledgment
445
586
  cmd: responseCmd,
@@ -454,7 +595,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
454
595
  }
455
596
  break;
456
597
  case 'pubrec':
457
- sendPacket({
598
+ generateAndSendPacket({
458
599
  // Send a publish response
459
600
  cmd: 'pubrel',
460
601
  messageId: packet.messageId,
@@ -467,7 +608,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
467
608
  emitEvent('acknowledged', session, packet);
468
609
  break;
469
610
  case 'pingreq':
470
- sendPacket({ cmd: 'pingresp' });
611
+ generateAndSendPacket({ cmd: 'pingresp' });
471
612
  break;
472
613
  case 'disconnect':
473
614
  disconnected = true;
@@ -483,22 +624,22 @@ function onSocket(socket, send, request, user, mqttSettings) {
483
624
  } catch (error) {
484
625
  emitEvent('error', error, socket, packet, session);
485
626
  mqttLog.error?.(error);
486
- sendPacket({
627
+ generateAndSendPacket({
487
628
  // Send a subscription acknowledgment
488
629
  cmd: 'disconnect',
489
630
  });
490
631
  }
491
- function sendPacket(packetData, path?) {
492
- const send_packet = generate(packetData, mqttOptions);
493
- send(send_packet);
494
- recordAction(send_packet.length, 'bytes-sent', path, packetMethodName(packetData), 'mqtt');
632
+ // analytics stay per subscriber even when the packet itself is shared across them
633
+ function sendPacket(packet, methodName, path?) {
634
+ send(packet);
635
+ recordAction(packet.length, 'bytes-sent', path, methodName, 'mqtt');
636
+ }
637
+ function generateAndSendPacket(packetData, path?) {
638
+ sendPacket(generate(packetData, mqttOptions), packetMethodName(packetData), path);
495
639
  }
496
640
  function packetMethodName(packet) {
497
641
  return packet.qos > 0 ? packet.cmd + ',qos=' + packet.qos : packet.cmd;
498
642
  }
499
- function serialize(data) {
500
- return serializeMessage(data, request);
501
- }
502
643
  });
503
644
  parser.on('error', (error) => {
504
645
  mqttLog.warn('MQTT parsing error, closing connection:', error.message);
@@ -1,8 +1,10 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { X509Certificate } from 'node:crypto';
3
+ import { isIP } from 'node:net';
3
4
  import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
4
5
  import * as env from '../utility/environment/environmentManager.ts';
5
6
  import { logger } from '../utility/logging/logger.ts';
7
+ import { bareHostViolation } from '../utility/nodeIdentity.ts';
6
8
  import { server } from './Server.ts';
7
9
 
8
10
  Object.defineProperty(server, 'hostname', {
@@ -17,20 +19,26 @@ function getCommonNameFromCert() {
17
19
  const certificatePath: string | undefined =
18
20
  env.get(CONFIG_PARAMS.OPERATIONSAPI_TLS_CERTIFICATE) || env.get(CONFIG_PARAMS.TLS_CERTIFICATE);
19
21
  if (certificatePath) {
20
- // we can use this to get the hostname if it isn't provided by config
21
- const certParsed = new X509Certificate(readFileSync(certificatePath));
22
- const subject = certParsed.subject;
23
- return (commonNameFromCert = subject?.match(/CN=(.*)/)?.[1] ?? null);
22
+ try {
23
+ // use this to get the hostname if it isn't provided by config
24
+ const subject = new X509Certificate(readFileSync(certificatePath)).subject;
25
+ return (commonNameFromCert = subject?.match(/CN=(.*)/)?.[1] ?? null);
26
+ } catch {
27
+ // a missing/unparseable cert file must not throw out of identity resolution — the caller
28
+ // falls back to another source. Do NOT cache the failure, so a later read (e.g. after the
29
+ // cert is created) can still pick up its common name.
30
+ return undefined;
31
+ }
24
32
  }
25
33
  }
26
34
 
27
35
  let nodeName: string | undefined;
28
36
  export function getThisNodeName(): string {
29
37
  if (nodeName) return nodeName; // if already determined, just return
30
- nodeName = env.get(CONFIG_PARAMS.NODE_HOSTNAME); // standard config
31
- if (nodeName) {
38
+ const configured = env.get(CONFIG_PARAMS.NODE_HOSTNAME);
39
+ if (configured) {
32
40
  const replicationHostname = env.get('replication_hostname');
33
- if (replicationHostname && replicationHostname !== nodeName) {
41
+ if (replicationHostname && replicationHostname !== configured) {
34
42
  // If these are both set and differ, the node identity is ambiguous. node.hostname
35
43
  // wins (it is what this node identifies as), but if it doesn't match the name this
36
44
  // node is registered under in hdb_nodes, replication for that name silently turns
@@ -38,18 +46,21 @@ export function getThisNodeName(): string {
38
46
  // node.hostname value — that's how a wrong identity (e.g. 'localhost') gets locked
39
47
  // in. Steer the operator to reconcile against the registered node name instead.
40
48
  logger.warn?.(
41
- `The node.hostname (${nodeName}) and replication.hostname (${replicationHostname}) configuration values are both set and differ. This node will identify as "${nodeName}". Ensure that name matches this node's row in system.hdb_nodes; if it does not, set node.hostname (or remove it to fall back to replication.hostname) to match the registered node name, otherwise replication for this node will be disabled.`
49
+ `The node.hostname (${configured}) and replication.hostname (${replicationHostname}) configuration values are both set and differ. This node will identify as "${configured}". Ensure that name matches this node's row in system.hdb_nodes; if it does not, set node.hostname (or remove it to fall back to replication.hostname) to match the registered node name, otherwise replication for this node will be disabled.`
42
50
  );
43
51
  }
44
- return nodeName;
45
52
  }
46
- // fallback to other means of getting the node name
53
+ // Use the first source that is a valid bare host (harper#2218). node.hostname and
54
+ // replication.hostname are rejected at the config boundary when invalid; the remaining derived
55
+ // sources are validated here and skipped — not fatal — if unusable, so a descriptive certificate
56
+ // common name or an empty value can never cache a corrupt identity or crash a request-time caller.
47
57
  nodeName =
48
- env.get('replication_hostname') ?? // for backwards compatibility
49
- urlToNodeName(env.get('replication_url') as string) ??
50
- getCommonNameFromCert() ??
51
- getHostFromListeningPort('operationsapi_network_secureport') ??
52
- getHostFromListeningPort('operationsapi_network_port') ??
58
+ asBareHost(configured, 'node.hostname') ??
59
+ asBareHost(env.get('replication_hostname'), 'replication.hostname') ?? // for backwards compatibility
60
+ asBareHost(replicationUrlHost(), 'the host in replication.url') ??
61
+ asBareHost(getCommonNameFromCert(), 'the certificate common name') ??
62
+ asBareHost(getHostFromListeningPort('operationsapi_network_secureport'), 'the operations API host') ??
63
+ asBareHost(getHostFromListeningPort('operationsapi_network_port'), 'the operations API host') ??
53
64
  '127.0.0.1';
54
65
  return nodeName;
55
66
  }
@@ -58,10 +69,66 @@ export function clearThisNodeName() {
58
69
  nodeName = undefined;
59
70
  }
60
71
 
72
+ // Reduce a node name to a host safe to compose a display URL from: strip a scheme/port (defensive —
73
+ // a configured node.hostname carrying those is now rejected at the config boundary) and bracket a
74
+ // bare IPv6 literal, without which the composed startup URLs are unparseable.
75
+ export function nodeNameToDisplayHost(name: string): string {
76
+ if (!name) return name;
77
+ const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(name);
78
+ const candidates = hasScheme ? [name] : [`http://${name}`, `http://[${name}]`];
79
+ for (const candidate of candidates) {
80
+ try {
81
+ const { hostname } = new URL(candidate);
82
+ if (hostname) return hostname;
83
+ } catch {}
84
+ }
85
+ return name;
86
+ }
87
+
88
+ export function getThisNodeHostname(): string {
89
+ return nodeNameToDisplayHost(getThisNodeName());
90
+ }
91
+
92
+ /**
93
+ * The host of replication.url, or undefined when it is unset or carries no usable host. Warns in the
94
+ * latter case for the same reason asBareHost does: a typo'd URL must not silently cost this node its
95
+ * identity. urlToNodeName itself stays quiet — it is also used for peer URLs, not just this node's.
96
+ */
97
+ function replicationUrlHost(): string | undefined {
98
+ const url: string | undefined = env.get(CONFIG_PARAMS.REPLICATION_URL);
99
+ if (!url) return undefined;
100
+ const host = urlToNodeName(url);
101
+ if (host === undefined) {
102
+ logger.warn?.(
103
+ `Ignoring replication.url (${JSON.stringify(url)}) as this node's identity: it is not a URL with a host. Falling back to the next available source, which may leave this node identifying as 127.0.0.1 and unable to replicate.`
104
+ );
105
+ }
106
+ return host;
107
+ }
108
+
109
+ /**
110
+ * A candidate identity source, or undefined when it is unset or not a bare host. Skipping is
111
+ * deliberate (see getThisNodeName) but silence is a diagnosis trap: a typo'd source that quietly
112
+ * resolves identity to 127.0.0.1 looks like a working node until replication fails, so say so.
113
+ */
114
+ function asBareHost(value: unknown, source: string): string | undefined {
115
+ if (value === undefined || value === null || value === '') return undefined;
116
+ const violation = bareHostViolation(value);
117
+ if (!violation) return value as string;
118
+ logger.warn?.(
119
+ `Ignoring ${source} (${JSON.stringify(value)}) as this node's identity: it ${violation}. Falling back to the next available source, which may leave this node identifying as 127.0.0.1 and unable to replicate. Set it to a bare hostname or IP literal.`
120
+ );
121
+ return undefined;
122
+ }
123
+
61
124
  function getHostFromListeningPort(key: string) {
62
125
  const port: string | undefined = env.get(key);
63
126
  const lastColon = port?.lastIndexOf?.(':');
64
- if (lastColon > 0) return port.slice(0, lastColon);
127
+ if (lastColon > 0) {
128
+ const host = port.slice(0, lastColon);
129
+ // A listen address stores IPv6 bracketed ("[::1]:9925"); the node identity is unbracketed.
130
+ return host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;
131
+ }
65
132
  }
66
133
  function getPortFromListeningPort(key: string) {
67
134
  const port: string | undefined = env.get(key);
@@ -72,18 +139,33 @@ function getPortFromListeningPort(key: string) {
72
139
 
73
140
  export function hostnameToUrl(hostname) {
74
141
  if (!hostname) return undefined;
142
+ // A bare IPv6 literal must be bracketed for the authority to parse: "ws://::1:9925" is rejected by
143
+ // the URL parser, "ws://[::1]:9925" is not. Hostnames and IPv4 pass through unchanged.
144
+ const host = isIP(hostname) === 6 ? `[${hostname}]` : hostname;
75
145
  let port = getPortFromListeningPort('replication_port');
76
- if (port) return `ws://${hostname}:${port}`;
146
+ if (port) return `ws://${host}:${port}`;
77
147
  port = getPortFromListeningPort('replication_secureport');
78
- if (port) return `wss://${hostname}:${port}`;
148
+ if (port) return `wss://${host}:${port}`;
79
149
  port = getPortFromListeningPort('operationsapi_network_port');
80
- if (port) return `ws://${hostname}:${port}`;
150
+ if (port) return `ws://${host}:${port}`;
81
151
  port = getPortFromListeningPort('operationsapi_network_secureport');
82
- if (port) return `wss://${hostname}:${port}`;
152
+ if (port) return `wss://${host}:${port}`;
83
153
  }
84
154
 
85
155
  export function urlToNodeName(nodeUrl?: string | URL): string | undefined {
86
- if (nodeUrl) return new URL(nodeUrl).hostname; // this the part of the URL that is the node name, as we want it to match common name in the certificate
156
+ if (!nodeUrl) return undefined;
157
+ try {
158
+ // the part of the URL that is the node name, matched against the certificate common name. URL
159
+ // keeps an IPv6 host bracketed ("[::1]"); the identity is stored unbracketed so it stays one
160
+ // canonical string across sources and net.isIP types it as an IP certificate SAN.
161
+ const host = new URL(nodeUrl).hostname;
162
+ // A hostless scheme ("mailto:", "data:", "file:", "urn:") parses but has an empty hostname —
163
+ // that is no name at all, not an identity, so treat it like a malformed URL.
164
+ if (host === '') return undefined;
165
+ return host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;
166
+ } catch {
167
+ return undefined; // a malformed URL yields no name — callers skip this source rather than crash
168
+ }
87
169
  }
88
170
 
89
171
  export function getThisNodeUrl() {