@harperfast/harper 5.2.10 → 5.3.0-alpha.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 (554) hide show
  1. package/agent/mcpTools.ts +1 -1
  2. package/agent/session.ts +25 -14
  3. package/bin/cliOperations.ts +46 -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 +59 -3
  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 +2414 -242
  14. package/components/ApplicationScope.ts +8 -0
  15. package/components/EntryHandler.ts +59 -39
  16. package/components/OptionsWatcher.ts +150 -74
  17. package/components/RuntimeModuleTracker.ts +38 -7
  18. package/components/Scope.ts +37 -15
  19. package/components/awaitRestart.ts +84 -0
  20. package/components/componentLoader.ts +340 -30
  21. package/components/componentPreparationLock.ts +16 -5
  22. package/components/mcp/adapters/harperHttp.ts +4 -0
  23. package/components/mcp/listChanged.ts +4 -0
  24. package/components/mcp/toolRegistry.ts +2 -0
  25. package/components/mcp/tools/operations.ts +9 -0
  26. package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
  27. package/components/operations.js +270 -78
  28. package/components/operationsValidation.js +49 -1
  29. package/components/status/ComponentStatusRegistry.ts +59 -0
  30. package/config/RootConfigWatcher.ts +80 -34
  31. package/config/configUtils.ts +291 -34
  32. package/config/harperConfigEnvVars.ts +170 -27
  33. package/config-root.schema.json +29 -0
  34. package/dataLayer/blobBackup.ts +160 -50
  35. package/dataLayer/delete.ts +6 -1
  36. package/dataLayer/harperBridge/ResourceBridge.ts +52 -8
  37. package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  38. package/dataLayer/hdbInfoController.ts +34 -1
  39. package/dataLayer/insert.ts +44 -1
  40. package/dataLayer/rocksdbBackup.ts +53 -10
  41. package/dataLayer/schema.ts +11 -1
  42. package/dataLayer/schemaDescribe.ts +8 -1
  43. package/dist/agent/mcpTools.js +1 -1
  44. package/dist/agent/mcpTools.js.map +1 -1
  45. package/dist/agent/session.d.ts +22 -0
  46. package/dist/agent/session.js +26 -15
  47. package/dist/agent/session.js.map +1 -1
  48. package/dist/bin/cliOperations.js +48 -9
  49. package/dist/bin/cliOperations.js.map +1 -1
  50. package/dist/bin/copyDb.d.ts +12 -1
  51. package/dist/bin/copyDb.js +248 -60
  52. package/dist/bin/copyDb.js.map +1 -1
  53. package/dist/bin/deploySetup.d.ts +2 -0
  54. package/dist/bin/deploySetup.js +11 -3
  55. package/dist/bin/deploySetup.js.map +1 -1
  56. package/dist/bin/harper.js +1 -1
  57. package/dist/bin/harper.js.map +1 -1
  58. package/dist/bin/help.js +4 -1
  59. package/dist/bin/help.js.map +1 -1
  60. package/dist/bin/lite.js +4 -1
  61. package/dist/bin/lite.js.map +1 -1
  62. package/dist/bin/restart.js +54 -5
  63. package/dist/bin/restart.js.map +1 -1
  64. package/dist/bin/run.js +4 -10
  65. package/dist/bin/run.js.map +1 -1
  66. package/dist/bin/upgrade.js +4 -3
  67. package/dist/bin/upgrade.js.map +1 -1
  68. package/dist/bin/workloadIdentity.d.ts +18 -0
  69. package/dist/bin/workloadIdentity.js +100 -0
  70. package/dist/bin/workloadIdentity.js.map +1 -0
  71. package/dist/components/Application.d.ts +139 -16
  72. package/dist/components/Application.js +2215 -267
  73. package/dist/components/Application.js.map +1 -1
  74. package/dist/components/ApplicationScope.d.ts +8 -0
  75. package/dist/components/ApplicationScope.js +7 -0
  76. package/dist/components/ApplicationScope.js.map +1 -1
  77. package/dist/components/EntryHandler.js +26 -10
  78. package/dist/components/EntryHandler.js.map +1 -1
  79. package/dist/components/OptionsWatcher.d.ts +1 -0
  80. package/dist/components/OptionsWatcher.js +141 -74
  81. package/dist/components/OptionsWatcher.js.map +1 -1
  82. package/dist/components/RuntimeModuleTracker.js +40 -6
  83. package/dist/components/RuntimeModuleTracker.js.map +1 -1
  84. package/dist/components/Scope.js +38 -13
  85. package/dist/components/Scope.js.map +1 -1
  86. package/dist/components/awaitRestart.d.ts +33 -0
  87. package/dist/components/awaitRestart.js +61 -0
  88. package/dist/components/awaitRestart.js.map +1 -0
  89. package/dist/components/componentLoader.d.ts +38 -1
  90. package/dist/components/componentLoader.js +279 -22
  91. package/dist/components/componentLoader.js.map +1 -1
  92. package/dist/components/componentPreparationLock.d.ts +5 -0
  93. package/dist/components/componentPreparationLock.js +14 -6
  94. package/dist/components/componentPreparationLock.js.map +1 -1
  95. package/dist/components/mcp/adapters/harperHttp.js +4 -0
  96. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  97. package/dist/components/mcp/listChanged.js +5 -0
  98. package/dist/components/mcp/listChanged.js.map +1 -1
  99. package/dist/components/mcp/toolRegistry.d.ts +1 -0
  100. package/dist/components/mcp/toolRegistry.js.map +1 -1
  101. package/dist/components/mcp/tools/operations.d.ts +5 -0
  102. package/dist/components/mcp/tools/operations.js +9 -0
  103. package/dist/components/mcp/tools/operations.js.map +1 -1
  104. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
  105. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  106. package/dist/components/operations.js +231 -77
  107. package/dist/components/operations.js.map +1 -1
  108. package/dist/components/operationsValidation.js +49 -1
  109. package/dist/components/operationsValidation.js.map +1 -1
  110. package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
  111. package/dist/components/status/ComponentStatusRegistry.js +63 -0
  112. package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
  113. package/dist/config/RootConfigWatcher.d.ts +7 -1
  114. package/dist/config/RootConfigWatcher.js +64 -27
  115. package/dist/config/RootConfigWatcher.js.map +1 -1
  116. package/dist/config/configUtils.d.ts +9 -1
  117. package/dist/config/configUtils.js +254 -33
  118. package/dist/config/configUtils.js.map +1 -1
  119. package/dist/config/harperConfigEnvVars.d.ts +16 -0
  120. package/dist/config/harperConfigEnvVars.js +162 -25
  121. package/dist/config/harperConfigEnvVars.js.map +1 -1
  122. package/dist/dataLayer/blobBackup.d.ts +49 -20
  123. package/dist/dataLayer/blobBackup.js +139 -50
  124. package/dist/dataLayer/blobBackup.js.map +1 -1
  125. package/dist/dataLayer/delete.js +1 -1
  126. package/dist/dataLayer/delete.js.map +1 -1
  127. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
  128. package/dist/dataLayer/harperBridge/ResourceBridge.js +51 -10
  129. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  130. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
  131. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
  132. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
  133. package/dist/dataLayer/hdbInfoController.d.ts +10 -0
  134. package/dist/dataLayer/hdbInfoController.js +30 -1
  135. package/dist/dataLayer/hdbInfoController.js.map +1 -1
  136. package/dist/dataLayer/insert.d.ts +9 -1
  137. package/dist/dataLayer/insert.js +30 -0
  138. package/dist/dataLayer/insert.js.map +1 -1
  139. package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
  140. package/dist/dataLayer/rocksdbBackup.js +45 -8
  141. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  142. package/dist/dataLayer/schema.js +8 -0
  143. package/dist/dataLayer/schema.js.map +1 -1
  144. package/dist/dataLayer/schemaDescribe.js +8 -1
  145. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  146. package/dist/index.d.ts +1 -1
  147. package/dist/index.js.map +1 -1
  148. package/dist/json/systemSchema.json +52 -0
  149. package/dist/resources/DatabaseTransaction.d.ts +55 -3
  150. package/dist/resources/DatabaseTransaction.js +460 -134
  151. package/dist/resources/DatabaseTransaction.js.map +1 -1
  152. package/dist/resources/LMDBTransaction.js +22 -4
  153. package/dist/resources/LMDBTransaction.js.map +1 -1
  154. package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
  155. package/dist/resources/PrimaryRocksDatabase.js +30 -2
  156. package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
  157. package/dist/resources/RecordEncoder.d.ts +20 -0
  158. package/dist/resources/RecordEncoder.js +94 -9
  159. package/dist/resources/RecordEncoder.js.map +1 -1
  160. package/dist/resources/RequestTarget.d.ts +2 -0
  161. package/dist/resources/RequestTarget.js.map +1 -1
  162. package/dist/resources/Resource.js +20 -11
  163. package/dist/resources/Resource.js.map +1 -1
  164. package/dist/resources/ResourceInterface.d.ts +20 -1
  165. package/dist/resources/ResourceInterface.js.map +1 -1
  166. package/dist/resources/RocksIndexStore.d.ts +6 -1
  167. package/dist/resources/RocksIndexStore.js +24 -10
  168. package/dist/resources/RocksIndexStore.js.map +1 -1
  169. package/dist/resources/RocksTransactionLogStore.d.ts +14 -1
  170. package/dist/resources/RocksTransactionLogStore.js +57 -17
  171. package/dist/resources/RocksTransactionLogStore.js.map +1 -1
  172. package/dist/resources/Table.d.ts +112 -8
  173. package/dist/resources/Table.js +1071 -197
  174. package/dist/resources/Table.js.map +1 -1
  175. package/dist/resources/auditStore.d.ts +11 -2
  176. package/dist/resources/auditStore.js +183 -18
  177. package/dist/resources/auditStore.js.map +1 -1
  178. package/dist/resources/blob.d.ts +129 -9
  179. package/dist/resources/blob.js +992 -114
  180. package/dist/resources/blob.js.map +1 -1
  181. package/dist/resources/branchDatabase.d.ts +48 -0
  182. package/dist/resources/branchDatabase.js +892 -0
  183. package/dist/resources/branchDatabase.js.map +1 -0
  184. package/dist/resources/crdt.js +50 -12
  185. package/dist/resources/crdt.js.map +1 -1
  186. package/dist/resources/databases.d.ts +150 -9
  187. package/dist/resources/databases.js +1247 -525
  188. package/dist/resources/databases.js.map +1 -1
  189. package/dist/resources/defineTable.d.ts +10 -2
  190. package/dist/resources/defineTable.js +9 -1
  191. package/dist/resources/defineTable.js.map +1 -1
  192. package/dist/resources/graphql.d.ts +1 -1
  193. package/dist/resources/graphql.js +50 -15
  194. package/dist/resources/graphql.js.map +1 -1
  195. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +1 -0
  196. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +6 -2
  197. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  198. package/dist/resources/longLivedTransactions.d.ts +71 -0
  199. package/dist/resources/longLivedTransactions.js +358 -0
  200. package/dist/resources/longLivedTransactions.js.map +1 -0
  201. package/dist/resources/models/backendRegistry.d.ts +26 -0
  202. package/dist/resources/models/backendRegistry.js +60 -2
  203. package/dist/resources/models/backendRegistry.js.map +1 -1
  204. package/dist/resources/models/bootstrap.d.ts +33 -1
  205. package/dist/resources/models/bootstrap.js +416 -31
  206. package/dist/resources/models/bootstrap.js.map +1 -1
  207. package/dist/resources/recordLock.d.ts +80 -0
  208. package/dist/resources/recordLock.js +184 -0
  209. package/dist/resources/recordLock.js.map +1 -0
  210. package/dist/resources/replayLogs.d.ts +14 -1
  211. package/dist/resources/replayLogs.js +152 -24
  212. package/dist/resources/replayLogs.js.map +1 -1
  213. package/dist/resources/replayLogsGuards.d.ts +94 -7
  214. package/dist/resources/replayLogsGuards.js +111 -7
  215. package/dist/resources/replayLogsGuards.js.map +1 -1
  216. package/dist/resources/search.js +158 -32
  217. package/dist/resources/search.js.map +1 -1
  218. package/dist/resources/transactionBroadcast.d.ts +1 -1
  219. package/dist/resources/transactionBroadcast.js +2 -2
  220. package/dist/resources/transactionBroadcast.js.map +1 -1
  221. package/dist/security/auth.js +61 -30
  222. package/dist/security/auth.js.map +1 -1
  223. package/dist/security/authn/oidc/claims.d.ts +22 -0
  224. package/dist/security/authn/oidc/claims.js +71 -0
  225. package/dist/security/authn/oidc/claims.js.map +1 -0
  226. package/dist/security/authn/oidc/identityToken.d.ts +27 -0
  227. package/dist/security/authn/oidc/identityToken.js +111 -0
  228. package/dist/security/authn/oidc/identityToken.js.map +1 -0
  229. package/dist/security/authn/oidc/jwks.d.ts +25 -0
  230. package/dist/security/authn/oidc/jwks.js +261 -0
  231. package/dist/security/authn/oidc/jwks.js.map +1 -0
  232. package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
  233. package/dist/security/authn/oidc/providers/generic.js +34 -0
  234. package/dist/security/authn/oidc/providers/generic.js.map +1 -0
  235. package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
  236. package/dist/security/authn/oidc/providers/githubActions.js +129 -0
  237. package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
  238. package/dist/security/authn/oidc/providers/index.d.ts +37 -0
  239. package/dist/security/authn/oidc/providers/index.js +24 -0
  240. package/dist/security/authn/oidc/providers/index.js.map +1 -0
  241. package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
  242. package/dist/security/authn/oidc/tokenExchange.js +306 -0
  243. package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
  244. package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
  245. package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
  246. package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
  247. package/dist/security/authn/oidc/types.d.ts +38 -0
  248. package/dist/security/authn/oidc/types.js +6 -0
  249. package/dist/security/authn/oidc/types.js.map +1 -0
  250. package/dist/security/certificateVerification/index.js +40 -11
  251. package/dist/security/certificateVerification/index.js.map +1 -1
  252. package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
  253. package/dist/security/certificateVerification/trustedIssuers.js +79 -0
  254. package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
  255. package/dist/security/certificateVerification/types.d.ts +1 -0
  256. package/dist/security/credentialProvenance.d.ts +35 -0
  257. package/dist/security/credentialProvenance.js +51 -0
  258. package/dist/security/credentialProvenance.js.map +1 -0
  259. package/dist/security/credentialRejection.d.ts +4 -0
  260. package/dist/security/credentialRejection.js +24 -0
  261. package/dist/security/credentialRejection.js.map +1 -0
  262. package/dist/security/deferredAuthentication.d.ts +36 -0
  263. package/dist/security/deferredAuthentication.js +70 -0
  264. package/dist/security/deferredAuthentication.js.map +1 -0
  265. package/dist/security/impersonation.d.ts +21 -0
  266. package/dist/security/impersonation.js +108 -9
  267. package/dist/security/impersonation.js.map +1 -1
  268. package/dist/security/jsLoader.d.ts +6 -0
  269. package/dist/security/jsLoader.js +75 -15
  270. package/dist/security/jsLoader.js.map +1 -1
  271. package/dist/security/keys.js +301 -71
  272. package/dist/security/keys.js.map +1 -1
  273. package/dist/security/operationScope.d.ts +21 -0
  274. package/dist/security/operationScope.js +36 -0
  275. package/dist/security/operationScope.js.map +1 -0
  276. package/dist/security/permissionsTranslator.js +21 -0
  277. package/dist/security/permissionsTranslator.js.map +1 -1
  278. package/dist/security/tokenAuthentication.d.ts +19 -1
  279. package/dist/security/tokenAuthentication.js +191 -10
  280. package/dist/security/tokenAuthentication.js.map +1 -1
  281. package/dist/security/user.js +4 -3
  282. package/dist/security/user.js.map +1 -1
  283. package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
  284. package/dist/server/DurableSubscriptionsSession.js +67 -10
  285. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  286. package/dist/server/REST.js +73 -0
  287. package/dist/server/REST.js.map +1 -1
  288. package/dist/server/graphqlQuerying.js +4 -0
  289. package/dist/server/graphqlQuerying.js.map +1 -1
  290. package/dist/server/http.d.ts +11 -0
  291. package/dist/server/http.js +88 -15
  292. package/dist/server/http.js.map +1 -1
  293. package/dist/server/itc/serverHandlers.js +8 -1
  294. package/dist/server/itc/serverHandlers.js.map +1 -1
  295. package/dist/server/jobs/jobProcess.js +6 -2
  296. package/dist/server/jobs/jobProcess.js.map +1 -1
  297. package/dist/server/jobs/jobs.js +4 -1
  298. package/dist/server/jobs/jobs.js.map +1 -1
  299. package/dist/server/liveSubscriptionAuth.d.ts +26 -4
  300. package/dist/server/liveSubscriptionAuth.js +105 -39
  301. package/dist/server/liveSubscriptionAuth.js.map +1 -1
  302. package/dist/server/loadRootComponents.js +49 -10
  303. package/dist/server/loadRootComponents.js.map +1 -1
  304. package/dist/server/mqtt.d.ts +2 -0
  305. package/dist/server/mqtt.js +165 -30
  306. package/dist/server/mqtt.js.map +1 -1
  307. package/dist/server/nodeName.d.ts +2 -0
  308. package/dist/server/nodeName.js +107 -23
  309. package/dist/server/nodeName.js.map +1 -1
  310. package/dist/server/serverHelpers/Headers.d.ts +25 -0
  311. package/dist/server/serverHelpers/Headers.js +139 -1
  312. package/dist/server/serverHelpers/Headers.js.map +1 -1
  313. package/dist/server/serverHelpers/contentTypes.d.ts +9 -0
  314. package/dist/server/serverHelpers/contentTypes.js +32 -25
  315. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  316. package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
  317. package/dist/server/serverHelpers/deployValidationState.js +9 -19
  318. package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
  319. package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
  320. package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
  321. package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
  322. package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
  323. package/dist/server/serverHelpers/registeredOperations.js +74 -21
  324. package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
  325. package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
  326. package/dist/server/serverHelpers/requestSanitization.js +20 -0
  327. package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
  328. package/dist/server/serverHelpers/serverHandlers.js +6 -3
  329. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  330. package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
  331. package/dist/server/serverHelpers/serverUtilities.js +90 -20
  332. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  333. package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
  334. package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
  335. package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
  336. package/dist/server/serverHelpers/uwsServer.js +19 -1
  337. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  338. package/dist/server/static.js +24 -28
  339. package/dist/server/static.js.map +1 -1
  340. package/dist/server/storageReclamation.d.ts +5 -0
  341. package/dist/server/storageReclamation.js +16 -0
  342. package/dist/server/storageReclamation.js.map +1 -1
  343. package/dist/server/threads/itc.d.ts +7 -2
  344. package/dist/server/threads/itc.js +5 -1
  345. package/dist/server/threads/itc.js.map +1 -1
  346. package/dist/server/threads/manageThreads.d.ts +26 -4
  347. package/dist/server/threads/manageThreads.js +517 -72
  348. package/dist/server/threads/manageThreads.js.map +1 -1
  349. package/dist/server/threads/socketRouter.js +89 -26
  350. package/dist/server/threads/socketRouter.js.map +1 -1
  351. package/dist/server/threads/threadHeapMemory.d.ts +2 -0
  352. package/dist/server/threads/threadHeapMemory.js +31 -0
  353. package/dist/server/threads/threadHeapMemory.js.map +1 -0
  354. package/dist/server/threads/threadServer.js +46 -15
  355. package/dist/server/threads/threadServer.js.map +1 -1
  356. package/dist/sqlEngine/config.d.ts +1 -3
  357. package/dist/sqlEngine/config.js +19 -16
  358. package/dist/sqlEngine/config.js.map +1 -1
  359. package/dist/sqlTranslator/index.d.ts +1 -1
  360. package/dist/sqlTranslator/index.js +30 -7
  361. package/dist/sqlTranslator/index.js.map +1 -1
  362. package/dist/upgrade/directives/5-3-0.d.ts +7 -0
  363. package/dist/upgrade/directives/5-3-0.js +148 -0
  364. package/dist/upgrade/directives/5-3-0.js.map +1 -0
  365. package/dist/upgrade/directives/directivesController.js +2 -1
  366. package/dist/upgrade/directives/directivesController.js.map +1 -1
  367. package/dist/utility/OperationFunctionCaller.js +2 -1
  368. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  369. package/dist/utility/common_utils.d.ts +16 -0
  370. package/dist/utility/common_utils.js +32 -6
  371. package/dist/utility/common_utils.js.map +1 -1
  372. package/dist/utility/componentNames.d.ts +8 -0
  373. package/dist/utility/componentNames.js +12 -1
  374. package/dist/utility/componentNames.js.map +1 -1
  375. package/dist/utility/environment/environmentManager.js +3 -6
  376. package/dist/utility/environment/environmentManager.js.map +1 -1
  377. package/dist/utility/environment/systemInformation.d.ts +1 -0
  378. package/dist/utility/environment/systemInformation.js +1 -0
  379. package/dist/utility/environment/systemInformation.js.map +1 -1
  380. package/dist/utility/errors/commonErrors.d.ts +2 -0
  381. package/dist/utility/errors/commonErrors.js +2 -0
  382. package/dist/utility/errors/commonErrors.js.map +1 -1
  383. package/dist/utility/errors/hdbError.d.ts +17 -0
  384. package/dist/utility/errors/hdbError.js +30 -1
  385. package/dist/utility/errors/hdbError.js.map +1 -1
  386. package/dist/utility/globalSchema.d.ts +18 -0
  387. package/dist/utility/hdbTerms.d.ts +16 -0
  388. package/dist/utility/hdbTerms.js +18 -2
  389. package/dist/utility/hdbTerms.js.map +1 -1
  390. package/dist/utility/logging/harper_logger.d.ts +2 -0
  391. package/dist/utility/logging/harper_logger.js +91 -14
  392. package/dist/utility/logging/harper_logger.js.map +1 -1
  393. package/dist/utility/logging/logRotator.js +76 -49
  394. package/dist/utility/logging/logRotator.js.map +1 -1
  395. package/dist/utility/nodeIdentity.d.ts +9 -0
  396. package/dist/utility/nodeIdentity.js +58 -0
  397. package/dist/utility/nodeIdentity.js.map +1 -0
  398. package/dist/utility/npmUtilities.js +9 -7
  399. package/dist/utility/npmUtilities.js.map +1 -1
  400. package/dist/utility/operationPermissions.d.ts +3 -1
  401. package/dist/utility/operationPermissions.js +16 -1
  402. package/dist/utility/operationPermissions.js.map +1 -1
  403. package/dist/utility/operation_authorization.d.ts +10 -7
  404. package/dist/utility/operation_authorization.js +212 -41
  405. package/dist/utility/operation_authorization.js.map +1 -1
  406. package/dist/utility/watchPath.d.ts +29 -0
  407. package/dist/utility/watchPath.js +68 -0
  408. package/dist/utility/watchPath.js.map +1 -0
  409. package/dist/utility/watcherFallback.d.ts +86 -0
  410. package/dist/utility/watcherFallback.js +278 -1
  411. package/dist/utility/watcherFallback.js.map +1 -1
  412. package/dist/validation/configValidator.d.ts +12 -0
  413. package/dist/validation/configValidator.js +199 -72
  414. package/dist/validation/configValidator.js.map +1 -1
  415. package/dist/validation/installValidator.js +12 -0
  416. package/dist/validation/installValidator.js.map +1 -1
  417. package/dist/validation/validationWrapper.d.ts +11 -0
  418. package/dist/validation/validationWrapper.js +16 -3
  419. package/dist/validation/validationWrapper.js.map +1 -1
  420. package/index.ts +2 -0
  421. package/json/systemSchema.json +52 -0
  422. package/npm-shrinkwrap.json +235 -233
  423. package/package.json +8 -7
  424. package/resources/DESIGN.md +114 -52
  425. package/resources/DatabaseTransaction.ts +522 -130
  426. package/resources/LMDBTransaction.ts +26 -4
  427. package/resources/PrimaryRocksDatabase.ts +29 -3
  428. package/resources/RecordEncoder.ts +103 -8
  429. package/resources/RequestTarget.ts +2 -0
  430. package/resources/Resource.ts +17 -9
  431. package/resources/ResourceInterface.ts +23 -0
  432. package/resources/RocksIndexStore.ts +30 -10
  433. package/resources/RocksTransactionLogStore.ts +78 -21
  434. package/resources/Table.ts +1107 -164
  435. package/resources/auditStore.ts +187 -21
  436. package/resources/blob.ts +1029 -110
  437. package/resources/branchDatabase.ts +941 -0
  438. package/resources/crdt.ts +76 -12
  439. package/resources/databases.ts +1354 -522
  440. package/resources/defineTable.ts +18 -2
  441. package/resources/graphql.ts +70 -16
  442. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +6 -2
  443. package/resources/longLivedTransactions.ts +360 -0
  444. package/resources/models/backendRegistry.ts +84 -2
  445. package/resources/models/bootstrap.ts +473 -28
  446. package/resources/recordLock.ts +253 -0
  447. package/resources/replayLogs.ts +147 -26
  448. package/resources/replayLogsGuards.ts +171 -8
  449. package/resources/search.ts +154 -32
  450. package/resources/transactionBroadcast.ts +3 -3
  451. package/security/auth.ts +68 -29
  452. package/security/authn/oidc/claims.ts +72 -0
  453. package/security/authn/oidc/identityToken.ts +129 -0
  454. package/security/authn/oidc/jwks.ts +260 -0
  455. package/security/authn/oidc/providers/generic.ts +40 -0
  456. package/security/authn/oidc/providers/githubActions.ts +137 -0
  457. package/security/authn/oidc/providers/index.ts +52 -0
  458. package/security/authn/oidc/tokenExchange.ts +300 -0
  459. package/security/authn/oidc/trustPolicyOperations.ts +343 -0
  460. package/security/authn/oidc/types.ts +41 -0
  461. package/security/certificateVerification/index.ts +54 -13
  462. package/security/certificateVerification/trustedIssuers.ts +76 -0
  463. package/security/certificateVerification/types.ts +1 -0
  464. package/security/credentialProvenance.ts +47 -0
  465. package/security/credentialRejection.ts +22 -0
  466. package/security/deferredAuthentication.ts +71 -0
  467. package/security/impersonation.ts +117 -12
  468. package/security/jsLoader.ts +81 -18
  469. package/security/keys.ts +298 -72
  470. package/security/operationScope.ts +33 -0
  471. package/security/permissionsTranslator.js +23 -0
  472. package/security/tokenAuthentication.ts +233 -12
  473. package/security/user.ts +4 -3
  474. package/server/DESIGN.md +183 -16
  475. package/server/DurableSubscriptionsSession.ts +71 -11
  476. package/server/REST.ts +79 -1
  477. package/server/graphqlQuerying.ts +4 -0
  478. package/server/http.ts +99 -18
  479. package/server/itc/serverHandlers.js +8 -1
  480. package/server/jobs/jobProcess.ts +8 -2
  481. package/server/jobs/jobs.ts +4 -1
  482. package/server/liveSubscriptionAuth.ts +129 -46
  483. package/server/loadRootComponents.js +50 -8
  484. package/server/mqtt.ts +179 -38
  485. package/server/nodeName.ts +103 -21
  486. package/server/serverHelpers/Headers.ts +131 -0
  487. package/server/serverHelpers/contentTypes.ts +29 -21
  488. package/server/serverHelpers/deployValidationState.ts +24 -13
  489. package/server/serverHelpers/operationAuthorizationState.ts +34 -3
  490. package/server/serverHelpers/registeredOperations.ts +79 -22
  491. package/server/serverHelpers/requestSanitization.ts +15 -0
  492. package/server/serverHelpers/serverHandlers.js +6 -3
  493. package/server/serverHelpers/serverUtilities.ts +104 -31
  494. package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
  495. package/server/serverHelpers/uwsServer.ts +17 -2
  496. package/server/static.ts +23 -29
  497. package/server/storageReclamation.ts +13 -0
  498. package/server/threads/itc.js +11 -1
  499. package/server/threads/manageThreads.js +526 -63
  500. package/server/threads/socketRouter.ts +81 -26
  501. package/server/threads/threadHeapMemory.ts +26 -0
  502. package/server/threads/threadServer.js +43 -15
  503. package/sqlTranslator/index.ts +31 -8
  504. package/studio/web/assets/{Chat-Br06zdMA.js → Chat-BnCBegQz.js} +1 -1
  505. package/studio/web/assets/{FloatingChat-BWImX5fA.js → FloatingChat-CoDW1ySS.js} +3 -3
  506. package/studio/web/assets/{apiToken-DN0nmDsq.js → apiToken-Bwk5BLXW.js} +1 -1
  507. package/studio/web/assets/{applications-kSxVoyeU.js → applications-DHxGi7JH.js} +1 -1
  508. package/studio/web/assets/{cssMode-C1JeufH5.js → cssMode-s0cWI-_M.js} +1 -1
  509. package/studio/web/assets/{editor-qoo9CrGO.js → editor-DNcRHK54.js} +1 -1
  510. package/studio/web/assets/{html-Dt4IIy04.js → html-Bdssedlg.js} +1 -1
  511. package/studio/web/assets/{htmlMode-DXgKKr4C.js → htmlMode-CoDlJ3fw.js} +1 -1
  512. package/studio/web/assets/{index-6onkYFOG.js → index-D6sxmFLR.js} +5 -5
  513. package/studio/web/assets/{index.lazy-BrCFnpNJ.js → index.lazy-tmU5BS8s.js} +1 -1
  514. package/studio/web/assets/{javascript-DNCQGUBc.js → javascript-B8meVSTH.js} +1 -1
  515. package/studio/web/assets/{jsonMode-CR6HWruP.js → jsonMode-DpIPd35T.js} +1 -1
  516. package/studio/web/assets/{languageServices-BM4fI4rS.js → languageServices-C_5FMJzQ.js} +1 -1
  517. package/studio/web/assets/{lspLanguageFeatures-DSa1ttcD.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
  518. package/studio/web/assets/{notifications-BHXLnh6x.js → notifications-CQf18QKb.js} +1 -1
  519. package/studio/web/assets/{notifications-CUmtIA6z.js → notifications-CvZivSbh.js} +1 -1
  520. package/studio/web/assets/{profile-8BeFSF3j.js → profile-DdOwtntb.js} +1 -1
  521. package/studio/web/assets/{regions-C8qR0HhD.js → regions-n69fwagr.js} +1 -1
  522. package/studio/web/assets/{register-B4n5i0SD.js → register-PfWTCXWB.js} +2 -2
  523. package/studio/web/assets/{setComponentFile-g0_B0lgX.js → setComponentFile-Bg6O7X0S.js} +1 -1
  524. package/studio/web/assets/{setup-B0CTj_Q5.js → setup-CUx_aUDl.js} +2 -2
  525. package/studio/web/assets/{status-C6Yib7-K.js → status-D7BVKqX9.js} +1 -1
  526. package/studio/web/assets/{toggleHighContrast-Dgta7bVi.js → toggleHighContrast-DBSyXzMr.js} +1 -1
  527. package/studio/web/assets/{tsMode-CH_jHvU-.js → tsMode-BByKCjBS.js} +1 -1
  528. package/studio/web/assets/{typescript-Co9LCXd5.js → typescript-DDLnLpw9.js} +1 -1
  529. package/studio/web/assets/{useEntityRestURL-wKC8NsC_.js → useEntityRestURL-31CHGaHk.js} +1 -1
  530. package/studio/web/assets/{workers-CWeLxCXA.js → workers-pR3jRY9D.js} +1 -1
  531. package/studio/web/assets/{xml-BadC-0Rk.js → xml-2iRnMhQO.js} +1 -1
  532. package/studio/web/assets/{yaml-BiUfxPbC.js → yaml-Bf92gJpd.js} +1 -1
  533. package/studio/web/index.html +1 -1
  534. package/upgrade/directives/5-3-0.ts +132 -0
  535. package/upgrade/directives/directivesController.ts +2 -1
  536. package/utility/OperationFunctionCaller.ts +2 -1
  537. package/utility/common_utils.ts +30 -5
  538. package/utility/componentNames.ts +12 -0
  539. package/utility/environment/environmentManager.ts +3 -7
  540. package/utility/environment/systemInformation.ts +7 -0
  541. package/utility/errors/commonErrors.ts +4 -0
  542. package/utility/errors/hdbError.ts +29 -0
  543. package/utility/hdbTerms.ts +17 -0
  544. package/utility/logging/harper_logger.ts +87 -14
  545. package/utility/logging/logRotator.ts +72 -45
  546. package/utility/nodeIdentity.ts +45 -0
  547. package/utility/npmUtilities.ts +10 -8
  548. package/utility/operationPermissions.ts +18 -1
  549. package/utility/operation_authorization.ts +231 -42
  550. package/utility/watchPath.ts +63 -0
  551. package/utility/watcherFallback.ts +270 -0
  552. package/validation/configValidator.ts +209 -72
  553. package/validation/installValidator.ts +15 -0
  554. package/validation/validationWrapper.ts +18 -4
@@ -59,7 +59,6 @@ exports.getPrimaryHostName = getPrimaryHostName;
59
59
  exports.hostnamesFromCert = hostnamesFromCert;
60
60
  exports.getHostnamesFromCertificate = getHostnamesFromCertificate;
61
61
  const path = __importStar(require("path"));
62
- const chokidar_1 = require("chokidar");
63
62
  const fs = __importStar(require("fs-extra"));
64
63
  const forge = __importStar(require("node-forge"));
65
64
  const net = __importStar(require("net"));
@@ -86,7 +85,10 @@ exports.getPrivateKeys = getPrivateKeys;
86
85
  const node_fs_1 = require("node:fs");
87
86
  const manageThreads_js_1 = require("../server/threads/manageThreads.js");
88
87
  const worker_threads_1 = require("worker_threads");
88
+ const watcherFallback_ts_1 = require("../utility/watcherFallback.js");
89
+ const watchPath_ts_1 = require("../utility/watchPath.js");
89
90
  const node_tls_1 = require("node:tls");
91
+ const trustedIssuers_ts_1 = require("./certificateVerification/trustedIssuers.js");
90
92
  const CERT_VALIDITY_DAYS = 3650;
91
93
  // Default interval (ms) for the periodic cert-file re-read safety net. The chokidar (inotify)
92
94
  // watcher is the fast path; this poll catches changes on filesystems where inotify is unreliable
@@ -192,6 +194,11 @@ const privateKeys = new Map();
192
194
  // Debounce window (ms) for rebuilding TLS secure contexts. Shared by the hdb_certificate
193
195
  // subscription and the private-key hot-reload trigger so both coalesce on the same cadence.
194
196
  const TLS_REBUILD_DEBOUNCE_MS = 1500;
197
+ // Self-retry backoff cap: a permanently bad record must not cost every selector on every
198
+ // thread a table scan + X509 parse per debounce interval, forever.
199
+ const TLS_FAILURE_RETRY_MAX_DELAY_MS = 300_000;
200
+ // While a failure signature is unchanged, repeat occurrences log a summary at most this often.
201
+ const TLS_FAILURE_SUMMARY_INTERVAL_MS = 3_600_000;
195
202
  // Debounced rebuild triggers, one per live server TLS selector (registered in createTLSSelector's
196
203
  // initialize). When a private key is hot-reloaded on this thread, every live selector re-runs
197
204
  // updateTLS so a secure context built with a stale key — or built before this key arrived — is
@@ -296,7 +303,9 @@ function loadCertificates() {
296
303
  logger.info?.(`Certificate ${certCn} at ${path} is older (${new Date(fileTimestamp)}) than the certificate in the database (${recordTimestamp > 1 ? new Date(recordTimestamp) : 'only self signed certificate available'})`);
297
304
  return;
298
305
  }
299
- promise = certificateTable.put({
306
+ // Returned so loadAndWatch can roll back its mtime latch if the write fails —
307
+ // assigned as well to preserve loadCertificates()'s awaited-return contract.
308
+ return (promise = certificateTable.put({
300
309
  name: certCn,
301
310
  uses: config.uses ?? (configKey.includes('operations') ? ['operations-api'] : []),
302
311
  ciphers: config.ciphers,
@@ -313,7 +322,7 @@ function loadCertificates() {
313
322
  valid_from: x509Cert.validFrom,
314
323
  valid_to: x509Cert.validTo,
315
324
  },
316
- });
325
+ }));
317
326
  }, ca ? 'certificate authority' : 'certificate');
318
327
  }
319
328
  }
@@ -364,27 +373,72 @@ const certificateWatchPollers = new Map();
364
373
  function loadAndWatch(path, loadCert, type) {
365
374
  let lastModified;
366
375
  const loadFile = (path, stats) => {
376
+ // The latch dedupes chokidar/poll but must mean "last successfully APPLIED", or a failed
377
+ // apply is deduplicated forever (#2382). Rollbacks are equality-guarded so an old failure
378
+ // can't unlatch a newer successful reload.
379
+ const previousModified = lastModified;
380
+ let modified;
367
381
  try {
368
382
  // chokidar's 'change' event omits stats unless alwaysStat is set (default off in v4), so
369
383
  // stat the file here when it's missing — otherwise the inotify fast path would throw and
370
384
  // silently never reload, leaving only the periodic poll to catch the change.
371
- let modified = (stats ?? (0, node_fs_1.statSync)(path)).mtimeMs;
385
+ modified = (stats ?? (0, node_fs_1.statSync)(path)).mtimeMs;
372
386
  if (modified && modified !== lastModified) {
373
387
  if (lastModified && worker_threads_1.isMainThread)
374
388
  logger.warn?.(`Reloading ${type}:`, path);
375
389
  lastModified = modified;
376
- loadCert(readPEM(path));
390
+ const applied = loadCert(readPEM(path));
391
+ if (typeof applied?.then === 'function') {
392
+ applied.catch((error) => {
393
+ logger.error?.(`Error applying ${type}:`, path, error);
394
+ if (lastModified === modified)
395
+ lastModified = previousModified;
396
+ });
397
+ }
377
398
  }
378
399
  }
379
400
  catch (error) {
380
401
  logger.error?.(`Error loading ${type}:`, path, error);
402
+ if (modified !== undefined && lastModified === modified)
403
+ lastModified = previousModified;
381
404
  }
382
405
  };
383
406
  if (fs.existsSync(path))
384
407
  loadFile(path, (0, node_fs_1.statSync)(path));
385
408
  else
386
409
  logger.error?.(`${type} file not found:`, path);
387
- (0, chokidar_1.watch)(path, { persistent: false }).on('change', loadFile);
410
+ const watchTarget = (0, watchPath_ts_1.resolveWatchTarget)(path);
411
+ let usingPolling = watchTarget.mustPoll;
412
+ let liveWatcher;
413
+ const openWatcher = () => {
414
+ const opened = (liveWatcher = (0, watcherFallback_ts_1.guardedWatch)(watchTarget.path, {
415
+ persistent: false,
416
+ ...(usingPolling ? watcherFallback_ts_1.POLLING_FALLBACK_OPTIONS : {}),
417
+ }));
418
+ opened
419
+ // The event carries the watched spelling, which is not the configured one once canonicalized;
420
+ // reload through the configured path so a retargeted link is followed.
421
+ .on('change', () => loadFile(path))
422
+ // chokidar emits 'error' unguarded for anything but ENOENT/ENOTDIR.
423
+ .on('error', (error) => {
424
+ if ((0, watcherFallback_ts_1.claimLostNativeWatchError)(error))
425
+ return;
426
+ if ((0, watcherFallback_ts_1.isWatcherExhaustionError)(error)) {
427
+ if (usingPolling || liveWatcher !== opened)
428
+ return;
429
+ (0, watcherFallback_ts_1.warnWatcherFallback)(path);
430
+ usingPolling = true;
431
+ Promise.resolve()
432
+ .then(() => opened.close())
433
+ .catch(() => { })
434
+ .then(openWatcher)
435
+ .catch((error) => logger.error?.(`Could not reopen the ${type} watch on polling:`, path, error));
436
+ return;
437
+ }
438
+ logger.error?.(`Error watching ${type}:`, path, error);
439
+ });
440
+ };
441
+ openWatcher();
388
442
  // Periodic re-read safety net. For certificates, this runs on the main thread only — workers
389
443
  // receive cert updates via the hdb_certificate table subscription, so polling the cert file on
390
444
  // every worker would be wasteful. Private keys are different: each worker loads its own key
@@ -421,13 +475,15 @@ function loadAndWatch(path, loadCert, type) {
421
475
  }
422
476
  }
423
477
  function getHost() {
424
- let url = (0, nodeName_ts_1.getThisNodeUrl)();
425
- if (url == null) {
478
+ // urlToNodeName returns undefined for a missing or malformed node/replication URL, so a bad
479
+ // replication.url falls back to the default host here rather than throwing during cert review.
480
+ const name = (0, nodeName_ts_1.urlToNodeName)((0, nodeName_ts_1.getThisNodeUrl)());
481
+ if (name == null) {
426
482
  const host = CERT_DOMAINS[0];
427
483
  logger.info?.('node url is missing from harperdb-config.yaml, using default host' + host);
428
484
  return host;
429
485
  }
430
- return (0, nodeName_ts_1.urlToNodeName)(url);
486
+ return name;
431
487
  }
432
488
  function getCommonName() {
433
489
  let node_name = (0, nodeName_ts_1.getThisNodeName)();
@@ -961,10 +1017,79 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
961
1017
  let subscribedTable = null;
962
1018
  let activeSubscription = null;
963
1019
  return (SNICallback.ready = new Promise((resolve, reject) => {
1020
+ // Pass-level failure before `.ready` settles still rejects (boot surfaces it); after,
1021
+ // the only safe response is to keep serving live state and retry.
1022
+ let readySettled = false;
1023
+ const settle = (value) => {
1024
+ readySettled = true;
1025
+ resolve(value);
1026
+ };
1027
+ // Self-retry state for passes with per-record failures: the signature identifies the
1028
+ // failure set, the delay backs off while it is unchanged, and summaries are rate-limited.
1029
+ let failureSignature = '';
1030
+ let failureRetryDelay = TLS_REBUILD_DEBOUNCE_MS;
1031
+ let failureRetryTimer;
1032
+ let failureRetryCount = 0;
1033
+ let failureLastSummaryAt = 0;
1034
+ const scheduleFailureRetry = () => {
1035
+ // An armed external rebuild (or an armed retry) will run a pass that re-arms if needed.
1036
+ if (failureRetryTimer || rebuildTimer)
1037
+ return;
1038
+ failureRetryTimer = setTimeout(() => {
1039
+ failureRetryTimer = undefined;
1040
+ updateTLS();
1041
+ }, failureRetryDelay).unref();
1042
+ failureRetryDelay = Math.min(failureRetryDelay * 2, TLS_FAILURE_RETRY_MAX_DELAY_MS);
1043
+ };
1044
+ // Log each failure on a new/changed failure set; while unchanged, stay silent apart from a
1045
+ // rate-limited summary — an un-throttled 1.5s retry would emit ~57k lines/day per selector.
1046
+ const reportFailures = (failures) => {
1047
+ const signature = failures
1048
+ .map(({ cert, error }) => `${cert.name}: ${error?.message}`)
1049
+ .sort()
1050
+ .join('; ');
1051
+ if (signature !== failureSignature) {
1052
+ failureSignature = signature;
1053
+ failureRetryDelay = TLS_REBUILD_DEBOUNCE_MS;
1054
+ failureRetryCount = 0;
1055
+ failureLastSummaryAt = Date.now();
1056
+ // A timer armed for the previous signature would run the next pass on its stale
1057
+ // (possibly maxed-out) delay; re-arm fresh for the new failure set.
1058
+ if (failureRetryTimer) {
1059
+ clearTimeout(failureRetryTimer);
1060
+ failureRetryTimer = undefined;
1061
+ }
1062
+ for (const { cert, error } of failures)
1063
+ logger.error?.('Error applying TLS for', cert.name, `on the '${type}' listener`, error);
1064
+ }
1065
+ else {
1066
+ failureRetryCount++;
1067
+ // Stays at error: a stuck rotation must keep an alertable signal — the retained cert
1068
+ // is aging toward its notAfter the whole time.
1069
+ if (Date.now() - failureLastSummaryAt >= TLS_FAILURE_SUMMARY_INTERVAL_MS) {
1070
+ failureLastSummaryAt = Date.now();
1071
+ logger.error?.(`TLS rebuild for the '${type}' listener is still failing for [${failures
1072
+ .map(({ cert }) => cert.name)
1073
+ .join(', ')}] after ${failureRetryCount} retries; serving the last good state`);
1074
+ }
1075
+ }
1076
+ };
1077
+ const clearFailureState = () => {
1078
+ if (failureSignature) {
1079
+ logger.warn?.(`TLS rebuild for the '${type}' listener recovered; all certificates applied`);
1080
+ }
1081
+ failureSignature = '';
1082
+ failureRetryDelay = TLS_REBUILD_DEBOUNCE_MS;
1083
+ failureRetryCount = 0;
1084
+ if (failureRetryTimer) {
1085
+ clearTimeout(failureRetryTimer);
1086
+ failureRetryTimer = undefined;
1087
+ }
1088
+ };
964
1089
  function updateTLS() {
965
1090
  try {
966
1091
  if (databases_ts_1.databases === undefined) {
967
- resolve();
1092
+ settle();
968
1093
  return;
969
1094
  }
970
1095
  if (databases_ts_1.databases.system?.hdb_certificate === undefined) {
@@ -992,19 +1117,19 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
992
1117
  scheduleRebuild();
993
1118
  return;
994
1119
  }
995
- // Guards above have returned before touching secureContexts/caCerts, so the retry
996
- // path never wipes a previously-published (non-empty) state before failing to
997
- // replace it.
998
- secureContexts.clear();
999
- caCerts.clear();
1120
+ // Transactional publication (#2382): build the whole replacement state off to the
1121
+ // side and reconcile the live maps only after the pass completes, so a failure of
1122
+ // any shape leaves the currently-served state intact.
1123
+ const candidateContexts = new Map();
1124
+ const candidateCAs = new Map();
1125
+ let candidateHasWildcards = false;
1000
1126
  let bestQuality = 0;
1001
- // Whether THIS pass produced a default context. The `defaultContext` closure variable
1002
- // is deliberately never reset (a transient zero-cert pass must keep serving the prior
1003
- // default while the retry below waits for certs to come back), so it can't be used to
1004
- // ask "did this pass find anything?" — after the first successful pass it is truthy
1005
- // forever, which would let a later transient zero-cert rebuild skip the retry and
1006
- // publish an empty certificates list (the #1998 symptom) on the post-boot path.
1127
+ let candidateDefault;
1128
+ // Whether THIS pass produced a default. The `defaultContext` closure variable is never
1129
+ // reset (a transient zero-cert pass keeps serving the prior default), so keying the
1130
+ // zero-cert retry off it would republish empty post-boot — the #1998 symptom.
1007
1131
  let defaultContextSetThisPass = false;
1132
+ const failedThisPass = [];
1008
1133
  // Track the actual table instance, not just whether we've ever subscribed: resetDatabases()
1009
1134
  // (copy_db, ITC restart handling) replaces databases.system.hdb_certificate with a new
1010
1135
  // table object, and a boolean flag would never re-subscribe to it, permanently losing
@@ -1035,24 +1160,28 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
1035
1160
  previousSubscription.then((subscription) => subscription?.end?.()).catch(() => { });
1036
1161
  }
1037
1162
  }
1038
- for (const cert of databases_ts_1.databases.system.hdb_certificate.search([])) {
1163
+ // One snapshot drives both loops: separate search() calls own separate read
1164
+ // snapshots, and a write committing between them could publish a renewed leaf
1165
+ // against the previous CA set.
1166
+ const certRecords = Array.from(databases_ts_1.databases.system.hdb_certificate.search([]));
1167
+ for (const cert of certRecords) {
1039
1168
  const certificate = cert.certificate;
1040
1169
  let certParsed;
1041
1170
  try {
1042
1171
  certParsed = new node_crypto_1.X509Certificate(certificate);
1043
1172
  }
1044
1173
  catch (error) {
1045
- // One unparseable record shouldn't abort the whole pass before any context is
1046
- // built — the second loop's per-cert try/catch doesn't cover this loop.
1047
- logger.error?.('Skipping unparseable certificate record', cert.name, error);
1174
+ // A pass failure like any other (throttle + retry). Its caCerts entry can't be
1175
+ // retained — the subject is unrecoverable from a PEM that won't parse.
1176
+ failedThisPass.push({ cert, error });
1048
1177
  continue;
1049
1178
  }
1050
1179
  if (cert.is_authority) {
1051
1180
  certParsed.asString = certificate;
1052
- caCerts.set(certParsed.subject, certificate);
1181
+ candidateCAs.set(certParsed.subject, certificate);
1053
1182
  }
1054
1183
  }
1055
- for (const cert of databases_ts_1.databases.system.hdb_certificate.search([])) {
1184
+ for (const cert of certRecords) {
1056
1185
  try {
1057
1186
  if (cert.is_authority) {
1058
1187
  continue;
@@ -1070,8 +1199,8 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
1070
1199
  const private_key = getPrivateKeyByName(cert.private_key_name);
1071
1200
  let certificate = cert.certificate;
1072
1201
  const certParsed = new node_crypto_1.X509Certificate(certificate);
1073
- if (caCerts.has(certParsed.issuer)) {
1074
- certificate += '\n' + caCerts.get(certParsed.issuer);
1202
+ if (candidateCAs.has(certParsed.issuer)) {
1203
+ certificate += '\n' + candidateCAs.get(certParsed.issuer);
1075
1204
  }
1076
1205
  if (!private_key || !certificate) {
1077
1206
  throw new Error('Missing private key or certificate for secure server');
@@ -1079,8 +1208,10 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
1079
1208
  const secureOptions = {
1080
1209
  ciphers: cert.ciphers,
1081
1210
  ticketKeys: (0, manageThreads_js_1.getTicketKeys)(),
1082
- availableCAs: caCerts, // preserve the record of caCerts even if not used for mTLS here
1083
- ca: mtlsOptions && Array.from(caCerts.values()),
1211
+ // the live map, not the candidate: its identity survives publication, so contexts
1212
+ // keep seeing the current CA set after later rebuilds
1213
+ availableCAs: caCerts,
1214
+ ca: mtlsOptions && Array.from(candidateCAs.values()),
1084
1215
  cert: certificate,
1085
1216
  key: private_key,
1086
1217
  key_file: cert.private_key_name,
@@ -1099,7 +1230,7 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
1099
1230
  secureContext.name = cert.name;
1100
1231
  secureContext.options = secureOptions;
1101
1232
  secureContext.quality = quality;
1102
- secureContext.certificateAuthorities = Array.from(caCerts);
1233
+ secureContext.certificateAuthorities = Array.from(candidateCAs);
1103
1234
  // we store the first 100 bytes of the certificate just for debug logging
1104
1235
  secureContext.certStart = certificate.toString().slice(0, 100);
1105
1236
  // we want to configure SNI handling to pick the right certificate based on all the registered SANs
@@ -1107,14 +1238,14 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
1107
1238
  for (let hostname of hostnames) {
1108
1239
  if (hostname) {
1109
1240
  if (hostname[0] === '*') {
1110
- hasWildcards = true;
1241
+ candidateHasWildcards = true;
1111
1242
  hostname = hostname.slice(1);
1112
1243
  }
1113
1244
  // we use this certificate if it has a higher quality than the existing one for this hostname
1114
- let existingCertQuality = secureContexts.get(hostname)?.quality ?? 0;
1245
+ let existingCertQuality = candidateContexts.get(hostname)?.quality ?? 0;
1115
1246
  logger.trace?.('Assigning TLS for hostname', hostname, 'if', quality, '>', existingCertQuality);
1116
1247
  if (quality > existingCertQuality) {
1117
- secureContexts.set(hostname, secureContext);
1248
+ candidateContexts.set(hostname, secureContext);
1118
1249
  }
1119
1250
  }
1120
1251
  else {
@@ -1124,54 +1255,147 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
1124
1255
  logger.trace?.('Adding TLS', secureContext.name, 'for', server?.ports || 'client', 'cert named', cert.name, 'hostnames', hostnames, 'quality', quality, 'best quality', bestQuality);
1125
1256
  if (quality > bestQuality /* && hasIpAddress*/) {
1126
1257
  // we use this certificate as the default if it has a higher quality than the existing one
1127
- SNICallback.defaultContext = defaultContext = secureContext;
1258
+ candidateDefault = secureContext;
1128
1259
  defaultContextSetThisPass = true;
1129
1260
  bestQuality = quality;
1130
- if (server) {
1131
- server.defaultContext = secureContext;
1132
- // note that we can not set the secure context on the server here, because this creates an
1133
- // indeterminate situation of whether openssl will use this certificate or the one from the SNI
1134
- // callback
1135
- //server.setSecureContext?.(server, secureOptions);
1136
- }
1137
1261
  }
1138
1262
  }
1139
1263
  catch (error) {
1140
- logger.error?.('Error applying TLS for', cert.name, error);
1264
+ failedThisPass.push({ cert, error });
1141
1265
  }
1142
1266
  }
1143
- if (liveReload && secureContexts.size === 0 && !defaultContextSetThisPass) {
1144
- // The not-loaded-yet guard above only covers the table object being absent, not the
1145
- // table being present but every row failing to apply (e.g. a private key not yet
1146
- // available on this thread, caught above per-cert). Resolving here would still write
1147
- // an empty `certificates:` list — the exact customer-visible symptom this PR exists to
1148
- // fix. Retry on the same debounce instead of publishing that state; a rebuild trigger
1149
- // (cert-table change or private-key reload, both already wired to scheduleRebuild) is
1150
- // almost certainly still coming on a normal boot, and this just avoids a window where
1151
- // we'd otherwise publish empty in the meantime.
1152
- //
1153
- // `!defaultContextSetThisPass` because an empty hostname map is not the same as "no TLS
1154
- // available": a cert whose hostnames resolve to [] (no usable SANs and no CN) builds no
1155
- // per-hostname entry but still sets a serviceable default context — that listener must
1156
- // resolve and serve via the default rather than retry forever for hostname entries that
1157
- // can't exist. It must be the per-pass flag, not the persistent `defaultContext` (see its
1158
- // declaration note): keying off the closure variable would disarm this retry for every
1159
- // pass after the first success, reopening the publish-empty window on live rebuilds.
1160
- //
1161
- // Gated on liveReload: transient, single-use selectors (getReplicationCert) legitimately
1162
- // resolve empty — e.g. the bootstrap check `if (!(await getReplicationCert())) { create
1163
- // one }` in generateCertAuthority's caller depends on an empty resolution meaning "no
1164
- // cert yet," and must not hang waiting for a cert that this exact call is about to create.
1165
- //
1166
- // Latched like the system-db wait above: this retries indefinitely on the debounce, and
1167
- // an unlatched warn would emit ~57k lines/day from a listener stuck this way.
1267
+ // Retain-last-good: a record still in the table whose build failed keeps its live
1268
+ // hostname entries and its default candidacy; deletion remains the way to drop them.
1269
+ // On an mTLS listener whose CA set changed, the retained pair is rebuilt against the
1270
+ // current trust material — new handshakes never see stale trust (established sessions
1271
+ // and outstanding tickets are unaffected, as on any build); if the rebuild fails the entries drop
1272
+ // unless nothing else is servable (the zero-cert guard below then keeps the old
1273
+ // state). Full contract in DESIGN.md "TLS hot-reload".
1274
+ const caSetUnchanged = (previous) => {
1275
+ const builtWith = previous.certificateAuthorities;
1276
+ if (!Array.isArray(builtWith))
1277
+ return candidateCAs.size === 0;
1278
+ return (builtWith.length === candidateCAs.size &&
1279
+ builtWith.every(([subject, pem]) => candidateCAs.get(subject) === pem));
1280
+ };
1281
+ const rebuiltRetentions = new Map();
1282
+ const retentionFailures = [];
1283
+ const retainable = (previous) => {
1284
+ // Without mTLS nothing consulted depends on the CA set — skip the rebuild, but
1285
+ // refresh the CA bookkeeping mirrored into socket metadata for fronting proxies.
1286
+ if (!mtlsOptions || caSetUnchanged(previous)) {
1287
+ previous.certificateAuthorities = Array.from(candidateCAs);
1288
+ return previous;
1289
+ }
1290
+ if (rebuiltRetentions.has(previous))
1291
+ return rebuiltRetentions.get(previous);
1292
+ try {
1293
+ const secureOptions = {
1294
+ ...previous.options,
1295
+ ticketKeys: (0, manageThreads_js_1.getTicketKeys)(),
1296
+ availableCAs: caCerts,
1297
+ ca: mtlsOptions && Array.from(candidateCAs.values()),
1298
+ };
1299
+ const rebuilt = tls.createSecureContext(secureOptions);
1300
+ rebuilt.name = previous.name;
1301
+ rebuilt.options = secureOptions;
1302
+ rebuilt.quality = previous.quality;
1303
+ rebuilt.certificateAuthorities = Array.from(candidateCAs);
1304
+ rebuilt.certStart = previous.certStart;
1305
+ rebuiltRetentions.set(previous, rebuilt);
1306
+ return rebuilt;
1307
+ }
1308
+ catch (error) {
1309
+ retentionFailures.push({
1310
+ cert: { name: `${previous.name} (retained-context rebuild)` },
1311
+ error,
1312
+ });
1313
+ rebuiltRetentions.set(previous, undefined);
1314
+ return undefined;
1315
+ }
1316
+ };
1317
+ for (const { cert } of failedThisPass) {
1318
+ // Ties go to the incumbent: a hostname the retained context already owned, and the
1319
+ // live default when it is the retained record, must not flip to an equal-quality
1320
+ // sibling because of a transient failure. A context retained only for a hostname
1321
+ // still needs strict > to become the default.
1322
+ const retain = (previous, hostname) => {
1323
+ const retained = retainable(previous);
1324
+ if (!retained)
1325
+ return;
1326
+ const previousQuality = retained.quality ?? 0;
1327
+ if (hostname !== undefined) {
1328
+ if (previousQuality >= (candidateContexts.get(hostname)?.quality ?? 0)) {
1329
+ candidateContexts.set(hostname, retained);
1330
+ }
1331
+ if (hostname[0] === '.')
1332
+ candidateHasWildcards = true;
1333
+ }
1334
+ const winsDefault = previous === defaultContext ? previousQuality >= bestQuality : previousQuality > bestQuality;
1335
+ if (winsDefault) {
1336
+ candidateDefault = retained;
1337
+ defaultContextSetThisPass = true;
1338
+ bestQuality = previousQuality;
1339
+ }
1340
+ };
1341
+ for (const [hostname, previous] of secureContexts) {
1342
+ if (previous.name === cert.name)
1343
+ retain(previous, hostname);
1344
+ }
1345
+ if (defaultContext?.name === cert.name)
1346
+ retain(defaultContext);
1347
+ }
1348
+ if (liveReload && candidateContexts.size === 0 && !defaultContextSetThisPass) {
1349
+ // Every row failed to apply (e.g. keys not yet on this thread). Publishing or
1350
+ // resolving would write an empty `certificates:` list (the #1998 symptom), so keep
1351
+ // live state and retry. `!defaultContextSetThisPass`, not the persistent
1352
+ // `defaultContext`: a cert with no usable hostnames still sets a serviceable
1353
+ // default (must resolve, not retry forever), while the closure variable is truthy
1354
+ // forever after the first success. Gated on liveReload: transient selectors
1355
+ // (getReplicationCert) legitimately resolve empty — bootstrap depends on that
1356
+ // meaning "no cert yet". Warn is latched: unlatched would emit ~57k lines/day.
1168
1357
  if (server && !server.tlsSelectorWarnedZeroCerts) {
1169
1358
  server.tlsSelectorWarnedZeroCerts = true;
1170
1359
  logger.warn?.(`TLS selector for the '${type}' listener resolved zero certificates; retrying every ${TLS_REBUILD_DEBOUNCE_MS}ms`);
1171
1360
  }
1172
- scheduleRebuild();
1361
+ if (failedThisPass.length > 0) {
1362
+ // Surface WHY the pass came up empty and put the retry on the backoff.
1363
+ reportFailures(failedThisPass.concat(retentionFailures));
1364
+ scheduleFailureRetry();
1365
+ }
1366
+ else {
1367
+ scheduleRebuild();
1368
+ }
1173
1369
  return;
1174
1370
  }
1371
+ // Publish: reconcile the live maps in place (their identity is aliased by
1372
+ // server.secureContexts and every context's availableCAs) and advance all default
1373
+ // references together so SNI and non-SNI traffic can't diverge.
1374
+ secureContexts.clear();
1375
+ for (const [hostname, context] of candidateContexts)
1376
+ secureContexts.set(hostname, context);
1377
+ caCerts.clear();
1378
+ for (const [subject, certificate] of candidateCAs)
1379
+ caCerts.set(subject, certificate);
1380
+ // only listener selectors publish: a one-shot client selector's pass may see no authority rows
1381
+ if (liveReload)
1382
+ (0, trustedIssuers_ts_1.publishTrustedAuthorities)(caCerts.values());
1383
+ hasWildcards = candidateHasWildcards;
1384
+ if (candidateDefault) {
1385
+ SNICallback.defaultContext = defaultContext = candidateDefault;
1386
+ // note that we can not set the secure context on the server here, because this creates an
1387
+ // indeterminate situation of whether openssl will use this certificate or the one from the SNI
1388
+ // callback
1389
+ if (server)
1390
+ server.defaultContext = candidateDefault;
1391
+ }
1392
+ if (failedThisPass.length > 0) {
1393
+ reportFailures(failedThisPass.concat(retentionFailures));
1394
+ scheduleFailureRetry();
1395
+ }
1396
+ else {
1397
+ clearFailureState();
1398
+ }
1175
1399
  // A successful pass ends any warn latches so a later recurrence logs again.
1176
1400
  if (server) {
1177
1401
  server.tlsSelectorWaitedForSystemDb = false;
@@ -1194,9 +1418,15 @@ function createTLSSelector(type, mtlsOptions, liveReload = true) {
1194
1418
  }
1195
1419
  }
1196
1420
  server?.secureContextsListeners.forEach((listener) => listener());
1197
- resolve(defaultContext);
1421
+ settle(defaultContext);
1198
1422
  }
1199
1423
  catch (error) {
1424
+ if (readySettled) {
1425
+ // Live state is untouched (candidates are pass-local): keep serving it and retry.
1426
+ reportFailures([{ cert: { name: `(${type} rebuild pass)` }, error }]);
1427
+ scheduleFailureRetry();
1428
+ return;
1429
+ }
1200
1430
  reject(error);
1201
1431
  }
1202
1432
  }