@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
package/server/DESIGN.md CHANGED
@@ -47,7 +47,8 @@ A request entering `http.ts` does **not** go through Fastify. The two `handleApp
47
47
  | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
48
48
  | `serverHelpers/Request.ts` | Wraps `IncomingMessage` with Harper-specific fields (user, response, headers). |
49
49
  | `serverHelpers/Headers.ts` | Header mutation/merge utilities. |
50
- | `serverHelpers/contentTypes.ts` | (de)serialization registry; `serialize`, `serializeMessage`, `getDeserializer`. |
50
+ | `serverHelpers/contentTypes.ts` | (de)serialization registry; `serialize`, `serializeMessage`, `getMessageSerializer`, `getDeserializer`. |
51
+ | `serverHelpers/sharedMessageEncoding.ts` | Per-message encoding shared across the subscribers of a topic — one serialization per (message, content type) and, for MQTT QoS 0, one PUBLISH packet per (payload, topic, protocol version). Keyed on the message object, which `transactionBroadcast` dispatches by identity to every subscription of a key. **Identity contract:** see below. |
51
52
  | `serverHelpers/serverUtilities.ts` | `OperationDefinition` and shared helpers. |
52
53
  | `serverHelpers/OperationFunctionObject.ts` | Wraps an operation handler with metadata. |
53
54
  | `serverHelpers/JSONStream.ts` | Streaming JSON output for large responses. |
@@ -58,6 +59,20 @@ A request entering `http.ts` does **not** go through Fastify. The two `handleApp
58
59
  | `serverRegistry.ts` | Trivial registry export. |
59
60
  | `status/` | Server status reporting (cluster status, per-port info). |
60
61
 
62
+ > **Subscription message identity contract.** `sharedMessageEncoding.ts` encodes a message once and
63
+ > reuses the bytes for every subscriber of the topic, keyed on the message object's identity. That is
64
+ > sound for every internal producer because each yields a fresh object per version — `transactionBroadcast`
65
+ > hands one `auditRecord` to every subscription, `auditStore`'s `getValue` memoizes the decode in its
66
+ > closure, and `primaryStore.getEntry` is version-guarded — so identity implies equal content. Pinned by
67
+ > `unitTests/resources/subscriptionValueIdentity.test.js`.
68
+ >
69
+ > Rather than leave that as a rule custom Resources have to know, the event's record `version` is part
70
+ > of the cache key: `DurableSubscriptionsSession` forwards it to the delivery listener, and an entry
71
+ > whose version does not match is re-encoded. A Resource that reuses one envelope but advances the
72
+ > version is therefore correct, not merely disallowed; one that supplies no version is not shared at
73
+ > all. Only mutating an object _without_ changing its version can serve stale bytes, and that is
74
+ > indistinguishable from re-sending the same message.
75
+
61
76
  ### Threads
62
77
 
63
78
  | File | Purpose |
@@ -74,6 +89,17 @@ calls fail with `ERR_HARPER_PROCESS_SHUTTING_DOWN`; scoped worker-type restarts
74
89
  `shutdownWorkersNow()` remains an immediate teardown: its worker shutdown messages are best-effort,
75
90
  and it force-terminates the remaining worker set rather than waiting for application drain hooks.
76
91
 
92
+ A rolling restart serves the _old_ code until each worker is replaced, and where the OS grants
93
+ `SO_REUSEPORT` the not-yet-replaced workers keep accepting connections for the whole restart — so a
94
+ component deploy is not live pool-wide until `restartWorkers()` resolves. For the overlapping types
95
+ (HTTP) it waits for each replacement to report `CHILD_STARTED` — including the ones that can only be
96
+ started after their predecessor releases its exclusive ports (Windows/macOS/Bun) — and reports how
97
+ many workers it left on the old code because a replacement never came up, versus how many
98
+ replacements never started after their predecessor was already gone. Other thread types start their
99
+ replacement without being awaited. Each wait is bounded by a per-worker startup backstop, so
100
+ resolution means "the restart finished", not "every worker is new". A caller that treats its own success as
101
+ "the component is live" must await it (see `deployComponent` in `components/operations.js`).
102
+
77
103
  > Workers receive `workerData.noServerStart = true` — never start the server inside a worker.
78
104
  >
79
105
  > `threadServer.listenOnDomainSocket()` skips a listener only when its path exceeds the platform's
@@ -105,7 +131,8 @@ Every entry is a top-level function or named const. Jump via go-to-symbol or `gr
105
131
  | `getPorts()` | Resolves listener options → list of `{port, secure}`. |
106
132
  | `httpServer()` | Main listener registration entry point. |
107
133
  | `getHTTPServer(port, secure, options)` | **The largest function in the file.** Creates/retrieves the underlying Node HTTP/HTTPS server. Wires `request`, `upgrade`, error handlers, TLS context, and the per-port middleware chain. |
108
- | `makeCallbackChain()` | Builds the per-port handler chain via `middlewareChain.topoSort`. |
134
+ | `makeCallbackChain()` | Builds the per-port handler chain via `middlewareChain.topoSort`, and records its resolved order for `get_status`. |
135
+ | `buildChains()` | Stores a built chain, and rebuilds every other already-built chain of that kind when the registration is on the `'all'` pseudo-port. See "Middleware ordering" below. |
109
136
  | `unhandled()` | Terminal 404 handler. |
110
137
  | `onRequest()` | Thin alias of `httpServer({requestOnly: true})`. |
111
138
  | `onUpgrade()` / `upgradeListeners` (const) | Register HTTP upgrade listener; underlying list. |
@@ -125,6 +152,19 @@ Components register listeners with optional `before: 'name'` / `after: 'name'` o
125
152
 
126
153
  The default WebSocket upgrade handler is registered automatically inside `onWebSocket()` the first time it runs for a given port.
127
154
 
155
+ **`port: 'all'` is a pseudo-port, and its chain is not the one that serves traffic.** Chain building
156
+ folds every `'all'` entry into each concrete port's chain, so `chains.all` exists but nothing
157
+ dispatches through it — `httpChain[port]`/`upgradeChains[port]`/`websocketChains[port]` are looked up
158
+ by the bound port on every request (Node, Bun, and uWS alike). A registration therefore has to
159
+ rebuild the chains it affects, not just its own key: `http.ts → buildChains()` rebuilds every
160
+ already-built chain of that kind whenever the registration is on `'all'`. Without that, an entry
161
+ registered on `'all'` after the concrete port's chain was built — the shape an application catch-all
162
+ mounted `after: 'rest'` takes, since `rest` registers first — updates only `chains.all` and never
163
+ reaches a request (#2418). Rebuilding is a pure function of the listener list and the port, so the
164
+ extra passes can only reproduce a port's order or extend it. `buildChains()` also writes the
165
+ `get_status` chain description in the same pass, which is what keeps that report from ever
166
+ describing an order a port isn't running (#1573).
167
+
128
168
  ### Application mounts (`host` / `urlPath` in the root config)
129
169
 
130
170
  An operator mounts an application by putting `host`/`urlPath` on its entry in the **root** config; `components/scopeMount.ts` models it and the loader threads it into every `Scope` for that application (both load paths — the root-config `package` recursion and the components-root directory scan).
@@ -154,16 +194,141 @@ must run on a worker, `registeredOperations.ts` carries that state in the same-p
154
194
  separately from the structured-cloned body. Never attach trusted dispatch state to an operation
155
195
  payload.
156
196
 
197
+ `server.registerOperation()` runs per-worker, so anything the **main** thread must later know about a
198
+ registered op has to ride the OPERATION_REGISTERED announcement — a module-local registry populated
199
+ during registration exists only in the worker that registered. The bridge carries two such facts
200
+ today: name→thread routing (for execution forwarding) and `grantable` (so `validateOperations` on
201
+ main will accept the name in a role's `operations` allowlist, for add_role/alter_role, impersonation,
202
+ and OIDC trust policies). Adding a third main-thread consumer of a worker-registered fact means
203
+ extending that message, not reading a registry that main never populated. Grantability is safe to
204
+ mirror because it only widens what an allowlist may _name_; enforcement stays on the worker's
205
+ `chooseOperation`.
206
+
157
207
  ## Resource ↔ HTTP boundary
158
208
 
159
209
  `REST.ts → http(request, nextHandler)` is the chief integration point: it takes a `Request`, asks the `Resources` registry for a match, builds a `RequestTarget`, and dispatches into the Resource class's static method. Cache headers are translated to `request.expiresAt` / `onlyIfCached` / `noCache` flags within the same function.
160
210
 
211
+ ### Deferred credential rejection (#2418)
212
+
213
+ `authentication` runs before route matching, so when it meets an `Authorization` header it cannot
214
+ resolve it does not yet know whether Harper or an application owns the URL. Rejecting there forces
215
+ an application to choose between two things it needs: Harper owning its own routes (status, REST
216
+ resources) and its own routes receiving their own credential scheme untouched.
217
+
218
+ So a rejection is recorded rather than answered:
219
+
220
+ - **Valid Harper credentials** authenticate normally and populate `request.user`. Unchanged.
221
+ - **No credentials** continue anonymously. Unchanged.
222
+ - **A syntactically valid credential Harper does not recognize** leaves `request.user` unset, leaves
223
+ the inbound `Authorization` header byte-for-byte intact, and records request-local state through
224
+ `security/deferredAuthentication.ts`. The state lives behind a module-private `Symbol`. Its
225
+ descriptor and value are immutable and non-enumerable, so downstream middleware cannot clear it
226
+ and it does not leak through request copies or serialization.
227
+ - **An internal authentication fault** — unreadable or malformed JWT key material, a storage failure,
228
+ an unexpected error type — is never deferred, and fails closed with the in-line 401.
229
+ - **The operations API** never defers: `request.isOperationsServer` short-circuits to the in-line
230
+ 401, because every operations route is Harper-owned and there is nothing to defer to.
231
+
232
+ **Rejection provenance is asserted, never inferred.** `security/credentialRejection.ts` holds a
233
+ module-private `Symbol` tag; only the code that actually concludes "this credential is unacceptable"
234
+ sets it, and `isCredentialRejection()` reads nothing else. Status ranges cannot carry that meaning:
235
+ `findAndValidateUser()` lazily loads the user cache, whose system-table searches raise a
236
+ default-status-400 `ClientError` when `system.hdb_role`/`system.hdb_user` is unavailable, so a 4xx
237
+ test would classify a storage outage as an unknown credential and hand it to application
238
+ authorization. The tag is set at exactly these points:
239
+
240
+ | Tagged rejection | Where |
241
+ | ------------------------------------------------------------------ | ------------------------------------------ |
242
+ | unknown user, inactive user, bad password | `security/user.ts → findAndValidateUser()` |
243
+ | JWT syntax, signature, expiry, not-before, subject/claim rejection | `tokenAuthentication.ts → validateToken()` |
244
+ | refresh-token hash mismatch, malformed scoped-token claims | `tokenAuthentication.ts` |
245
+ | an `Authorization` scheme Harper does not implement | `security/auth.ts` |
246
+
247
+ `validateToken()` separates the two in the same catch: `jsonwebtoken` reports unusable key material
248
+ through the very same `JsonWebTokenError` type it uses for a forged token, so the public key is
249
+ validated as key material before `jwt.verify()` runs and a residual key-material message is treated
250
+ as a fault. An untagged error propagates unmasked.
251
+
252
+ The Bearer path's refresh-token probe follows the same rule. When operation-token validation says
253
+ `invalid token`, authentication retries the credential as a refresh token; a fault raised by that
254
+ retry propagates, and only an ordinary _tagged_ refresh rejection restores the original
255
+ operation-token rejection for deferral. An in-line fail-closed response logs the original fault
256
+ server-side and returns the same generic authentication failure a rejected credential gets, so
257
+ internal detail never reaches an unauthenticated client.
258
+
259
+ Any layer that establishes Harper owns the route then settles the deferred state before doing work:
260
+
261
+ | Layer | Where |
262
+ | --------------------------------------- | ------------------------------------------------------------------ |
263
+ | `REST.ts → http()` | after `resources.getMatch` succeeds (and for the OpenAPI document) |
264
+ | `REST.ts` WebSocket handler | after `resources.getMatch(url, 'ws')` succeeds |
265
+ | `graphqlQuerying.ts` | after the `/graphql` prefix match, ahead of its error mapping |
266
+ | `static.ts` | after a static file entry matches |
267
+ | `mqtt.ts` WebSocket handler | once the pending HTTP chain settles, before the first packet |
268
+ | `components/mcp/adapters/harperHttp.ts` | after the WebSocket hand-off, before the body is read |
269
+
270
+ **Every Harper-owned handler registered `after: 'authentication'` owes this settlement**, because
271
+ declining to call `nextHandler` is precisely the moment ownership is settled. A handler that skips
272
+ it serves an unrecognized credential as anonymous — the MCP application mount did exactly that, and
273
+ returned 200 for an `initialize` the base revision answered with 401.
274
+
275
+ Settlement goes through `settleDeferredCredentialRejection()`, which returns the response descriptor
276
+ `security/auth.ts` used to return in line: status 401 and `serializeMessage({error: message}, request)`
277
+ in the request's negotiated content type. That matters because an owner's own error mapping is not
278
+ that contract — REST renders a thrown error as an RFC 9457 Problem Details document and GraphQL as
279
+ `{errors:[{message}]}` — and a rejected credential never reached either before deferral existed.
280
+ `assertNoDeferredCredentialRejection()` is the throwing form, for a WebSocket upgrade that has no
281
+ descriptor to return. A WebSocket owner cannot read the state synchronously: `server/http.ts` starts
282
+ `httpChain[port](request)` and invokes the WebSocket chain with the still-pending completion, so
283
+ authentication has not yet classified the credential. `mqtt.ts` therefore settles on that promise —
284
+ the same one the session's principal resolves from — and closes the socket from its rejection, while
285
+ its frame handlers still attach synchronously. The deferred status is pinned to 401 regardless of the underlying error's own
286
+ status, so a 403 `token expired` reads exactly as it did before. A Harper-owned route therefore behaves identically to the pre-deferral
287
+ build, protected or public: an unknown credential can never buy access an anonymous caller would
288
+ have received, and can never reach an application catch-all. Only a URL that reached
289
+ `nextHandler` — one no Harper route owns — carries the original header onward.
290
+
291
+ The contract is route-ownership-based, not path-based. There is no exemption list, no carrier
292
+ header, no credential rename, and no pre-auth stripping shim.
293
+
294
+ **Authentication does not re-decorate a 401 it did not raise.** `security/auth.ts` post-processes any
295
+ 401 coming back up the chain — overwriting `WWW-Authenticate` with `Basic`, or rewriting the status to
296
+ a 302 at `resources.loginPath` for a browser. The in-line rejection deferral replaced returned before
297
+ that code, so a settled rejection must skip it to stay wire-identical; and a 401 an application
298
+ catch-all raised for its own scheme (a WooCommerce or Bearer challenge) is that application's to make.
299
+ Both cases are keyed on the deferred state, so a request that deferred nothing keeps the existing
300
+ behavior exactly. The #1565 identity floor still applies either way — it is stamped in
301
+ `applyResponseHeaders`, not in the challenge rewriting.
302
+
303
+ **The identity cache floor survives the legacy Fastify fallbacks.** A response produced under a
304
+ deferred credential is credential-dependent (#1565), so `authentication` stamps
305
+ `Cache-Control: private, no-cache` and `Vary: Authorization, Cookie` on it. Before deferral an
306
+ unrecognized credential could not reach a fallback at all, so this was unreachable. All three adapters
307
+ now reconcile through one policy, `Headers.ts → mergeChainHeadersIntoFallback()`: Fastify wins every
308
+ single-valued header it set, `Vary` is unioned, and the private scope is re-applied unless the final
309
+ response explicitly opts into shared caching (`public`/`s-maxage`).
310
+
311
+ `Set-Cookie` is the deliberate exception to Fastify-wins. It is a list-valued field, so the chain's
312
+ cookies are appended beside Fastify's and de-duplicated by exact value, never by cookie name. A cookie
313
+ is identified by name _plus_ `Domain` _plus_ `Path` (RFC 6265 §5.3), so collapsing by name drops
314
+ legitimately distinct cookies — a same-name pair scoped to `/` and `/wp-admin`, or a `Max-Age=0`
315
+ deletion paired with a set. Exact value is also what makes Node's `writeHead` re-merge idempotent,
316
+ because that path sees the chain's own cookie already on the response. Two cookies that do share a
317
+ full identity both reach the client, chain last, and the user agent resolves them last-wins.
318
+
319
+ Bun and uWS rebuild their headers from Fastify's reply and merge once. Node hands Fastify the same
320
+ `ServerResponse` the chain's headers were copied onto, so copying is not enough — a route calling
321
+ `reply.header('Cache-Control', …)` replaces the floor outright and can make a credential-dependent
322
+ response shared-cacheable. `bridgeChainHeadersToNodeResponse()` therefore runs the same merge from a
323
+ `writeHead` interception, the last point the header set is still mutable and the one Node also routes
324
+ implicit headers through.
325
+
161
326
  ### Response Cache-Control / Vary policy (#1518, #1565)
162
327
 
163
328
  Three tiers, applied in two places:
164
329
 
165
330
  1. **App/resource explicit** — a `Cache-Control` set by the resource (or `@table(cacheControl: "...")` for anonymous reads, emitted in `REST.ts → http()`) always wins. The declaration is required: anonymous readability alone never emits shared-cache headers, because a request-attribute-gated `allowRead` (IP, headers) would make inferred `public` unsound.
166
- 2. **Identity floor** — `security/auth.ts → applyResponseHeaders` stamps `Cache-Control: private, no-cache` + `Vary: Authorization` (+ `Cookie` when sessions are on) on any response where a principal was resolved or credentials were rejected (401), _unless_ the app opted into shared caching with `public`/`s-maxage` (the RFC 9111 opt-in).
331
+ 2. **Identity floor** — `security/auth.ts → applyResponseHeaders` stamps `Cache-Control: private, no-cache` + `Vary: Authorization` (+ `Cookie` when sessions are on) on any response where a principal was resolved, credentials were rejected (401), or a credential rejection was deferred (#2418 — the application answered using the header Harper passed through, so the response is credential-dependent at a plain 200), _unless_ the app opted into shared caching with `public`/`s-maxage` (the RFC 9111 opt-in).
167
332
  3. **CORS partitioning** — when CORS is enabled, every response gets `Vary: Origin` (the ACAO header is reflected per-origin, and its absence on no-Origin requests is origin-dependent too).
168
333
 
169
334
  The `@table(cacheControl:)` value is persisted on the primary-key attribute (like `expiration`), so all threads and future boots see it; `resources/databases.ts → table()` treats `null` as "schema explicitly has none" (clears on reload) and `undefined` as "caller is not schema-defining" (no clobber from `add_attribute`/cluster schema events).
@@ -172,19 +337,21 @@ The `@table(cacheControl:)` value is persisted on the primary-key attribute (lik
172
337
 
173
338
  ## "Where is X" cheat sheet
174
339
 
175
- | Question | Where |
176
- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
177
- | Where do I register a new HTTP handler? | `http.ts → httpServer()` (or `onRequest()` for the request-only form) |
178
- | Where do I register a WebSocket handler? | `http.ts → onWebSocket()` |
179
- | How does `before`/`after` middleware ordering work? | `middlewareChain.ts → topoSort` |
180
- | Where does PROXY protocol get parsed? | `serverHelpers/proxyProtocol.ts` (applied by `http.ts → enableProxyProtocol` / `createH2CProxyFront`) |
181
- | How does an app read forwarded TLS facts (JA3/JA4, ALPN, SNI, mTLS cert)? | `request.connectionInfo` (`ConnectionInfo` from `serverHelpers/proxyProtocol.ts`); only set from a trusted PROXY v2 header on the UDS mirror, never from a request header |
182
- | Where is the REST request → Resource dispatch? | `REST.ts → http()` |
183
- | Where is the operations API request handled? | `operationsServer.ts → handler` |
184
- | How are content types (de)serialized? | `serverHelpers/contentTypes.ts` |
185
- | Where do durable subscriptions live? | `DurableSubscriptionsSession.ts` |
186
- | How are sockets dispatched to worker threads? | `threads/socketRouter.ts` |
187
- | Where is the Operations API wired into Fastify? | `operationsServer.ts → buildServer` |
340
+ | Question | Where |
341
+ | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
342
+ | Where do I register a new HTTP handler? | `http.ts → httpServer()` (or `onRequest()` for the request-only form) |
343
+ | Where do I register a WebSocket handler? | `http.ts → onWebSocket()` |
344
+ | How does `before`/`after` middleware ordering work? | `middlewareChain.ts → topoSort` |
345
+ | Where does PROXY protocol get parsed? | `serverHelpers/proxyProtocol.ts` (applied by `http.ts → enableProxyProtocol` / `createH2CProxyFront`) |
346
+ | How does an app read forwarded TLS facts (JA3/JA4, ALPN, SNI, mTLS cert)? | `request.connectionInfo` (`ConnectionInfo` from `serverHelpers/proxyProtocol.ts`); only set from a trusted PROXY v2 header on the UDS mirror, never from a request header |
347
+ | Why does mTLS revocation checking look up the client cert's issuer itself? | `security/certificateVerification/trustedIssuers.ts` — Node only completes a peer chain from the _listener_ context's store, but Harper's client CAs live on SNI contexts (`security/keys.ts → createTLSSelector`), so a resumed TLS session (and Node 26.8.0/26.8.1, nodejs/node#65579) exposes the leaf alone; `verifyCertificate` recovers the issuer from the published CA set and otherwise applies `failureMode` |
348
+ | Where is the REST request → Resource dispatch? | `REST.ts → http()` |
349
+ | Where is the operations API request handled? | `operationsServer.ts → handler` |
350
+ | How are content types (de)serialized? | `serverHelpers/contentTypes.ts` |
351
+ | Why doesn't every MQTT subscriber re-serialize the message it receives? | `serverHelpers/sharedMessageEncoding.ts` (memoized on the message object the fan-out shares); consumed by the outbound listener in `mqtt.ts` |
352
+ | Where do durable subscriptions live? | `DurableSubscriptionsSession.ts` |
353
+ | How are sockets dispatched to worker threads? | `threads/socketRouter.ts` |
354
+ | Where is the Operations API wired into Fastify? | `operationsServer.ts → buildServer` |
188
355
 
189
356
  ---
190
357
 
@@ -32,6 +32,24 @@ function getDurableSession() {
32
32
  return _DurableSession;
33
33
  }
34
34
  let _LastWill: any;
35
+ /**
36
+ * A scoped token's only revocation is expiry, so its will must not publish past it. Keyed off the
37
+ * persisted will principal (not any live session user) so both will paths agree on one source.
38
+ * Fails closed: a scoped will with no recorded expiry is treated as expired rather than published.
39
+ */
40
+ function isWillFromExpiredScopedToken(will: any): boolean {
41
+ const user = will?.user;
42
+ if (!user?._scopedToken) return false;
43
+ return !user.authExpiresAt || user.authExpiresAt * 1000 <= Date.now();
44
+ }
45
+
46
+ /** Drops the runtime-only pre-expanded operations Set so the permission set is storage-safe. */
47
+ function stripRuntimePermissionState(permission: any): any {
48
+ if (!permission || typeof permission !== 'object') return permission;
49
+ const { _expandedOperations, ...durable } = permission;
50
+ return durable;
51
+ }
52
+
35
53
  function getLastWill() {
36
54
  if (!_LastWill) {
37
55
  _LastWill = table({
@@ -56,13 +74,23 @@ if (getWorkerIndex() === 0) {
56
74
  for await (const will of getLastWill().search({})) {
57
75
  const data = will.data;
58
76
  const message = { ...will };
59
- if (message.user?.username) message.user = await (server as any).getUser(message.user.username);
60
77
  try {
61
- await publishMessage(message, data, message);
78
+ if (message.user?._scopedToken) {
79
+ // A scoped token's username is attribution only; never rehydrate it by name (that could
80
+ // substitute a real principal). The will carries the token's own downgraded role.
81
+ if (!isWillFromExpiredScopedToken(message)) {
82
+ await publishMessage(message, data, message);
83
+ } else warn('Dropping will from an expired scoped token', data);
84
+ } else {
85
+ if (message.user?.username) message.user = await (server as any).getUser(message.user.username);
86
+ await publishMessage(message, data, message);
87
+ }
88
+ await getLastWill().delete(will.id);
62
89
  } catch {
90
+ // One will's publish/delete failure must not abort replay of the rest; the row stays and
91
+ // is retried on the next restart.
63
92
  warn('Failed to publish will', data);
64
93
  }
65
- getLastWill().delete(will.id);
66
94
  }
67
95
  })();
68
96
  }
@@ -125,7 +153,22 @@ export async function getSession({
125
153
  }
126
154
  if (will) {
127
155
  will.id = sessionId;
128
- will.user = { username: user?.username };
156
+ // A scoped-token bearer's will must carry the token's own role and expiry: its username is
157
+ // attribution only and cannot be rehydrated from hdb_user at replay time. Persist only the
158
+ // durable permission fields — not the runtime-only _expandedOperations Set, which is rebuilt
159
+ // on read and would not round-trip through storage.
160
+ will.user = user?._scopedToken
161
+ ? {
162
+ username: user.username,
163
+ _scopedToken: true,
164
+ authExpiresAt: user.authExpiresAt,
165
+ role: user.role && {
166
+ role: user.role.role,
167
+ id: user.role.id,
168
+ permission: stripRuntimePermissionState(user.role.permission),
169
+ },
170
+ }
171
+ : { username: user?.username };
129
172
  // Must be durably persisted before CONNACK is sent (getSession() resolving is what lets
130
173
  // mqtt.ts send CONNACK). Otherwise a client that connects and then disconnects abruptly
131
174
  // (no DISCONNECT packet) can race ahead of this write: SubscriptionsSession.disconnect()
@@ -155,7 +198,7 @@ type Acknowledgement = {
155
198
  };
156
199
 
157
200
  class SubscriptionsSession {
158
- listener: (message, subscription, timestamp, qos) => any;
201
+ listener: (topic, message, messageId, subscription, version?) => any;
159
202
  sessionId: any;
160
203
  user: any;
161
204
  request: any;
@@ -303,7 +346,16 @@ class SubscriptionsSession {
303
346
  let path = update.id;
304
347
  if (Array.isArray(path)) path = keyArrayToString(path);
305
348
  if (path == null) path = '';
306
- const result = await this.listener(resourcePath + '/' + path, update.value, messageId, subscriptionRequest);
349
+ // the version is forwarded so the delivery side can tell a store-sourced event (whose
350
+ // value is a fresh object per version) from an app-yielded one that may be a reused
351
+ // mutable envelope — only the former is safe to encode once and share
352
+ const result = await this.listener(
353
+ resourcePath + '/' + path,
354
+ update.value,
355
+ messageId,
356
+ subscriptionRequest,
357
+ update.version
358
+ );
307
359
  if (result === false) break;
308
360
  if (this.awaitingAcks?.size > AWAITING_ACKS_HIGH_WATER_MARK) {
309
361
  // slow it down if we are getting too far ahead in acks
@@ -372,7 +424,7 @@ class SubscriptionsSession {
372
424
  }
373
425
  return context;
374
426
  }
375
- setListener(listener: (message) => any) {
427
+ setListener(listener: (topic, message, messageId, subscription, version?) => any) {
376
428
  this.listener = listener;
377
429
  }
378
430
  disconnect(clientTerminated) {
@@ -382,8 +434,11 @@ class SubscriptionsSession {
382
434
  try {
383
435
  if (!clientTerminated) {
384
436
  const will = await getLastWill().get(this.sessionId);
385
- if (will) {
386
- await publishMessage(will, will.data, context);
437
+ if (will && !isWillFromExpiredScopedToken(will)) {
438
+ // A scoped will authorizes under its own embedded role, never the disconnecting
439
+ // session's user (which may be a later same-clientId reconnect).
440
+ const willContext = will.user?._scopedToken ? { ...context, user: will.user } : context;
441
+ await publishMessage(will, will.data, willContext);
387
442
  }
388
443
  }
389
444
  } finally {
@@ -413,10 +468,15 @@ async function publishMessage(message: any, data: any, context: any) {
413
468
  message = { ...message, data, async: true };
414
469
  context.authorize = true;
415
470
  const entry = resources.getMatch(topic, 'mqtt');
416
- if (!entry)
417
- throw new Error(
471
+ if (!entry) {
472
+ // Typed like addSubscription's identical miss, so a protocol layer can map it to a specific
473
+ // code rather than a generic failure.
474
+ const notFoundError: any = new Error(
418
475
  `Can not publish to topic ${topic} as it does not exist, no resource has been defined to handle this topic`
419
476
  );
477
+ notFoundError.statusCode = 404;
478
+ throw notFoundError;
479
+ }
420
480
  message.url = entry.relativeURL;
421
481
  const target = new RequestTarget(entry.relativeURL);
422
482
  if (entry.params) Object.assign(target, entry.params); // bind parameterised path segments (e.g. :id, *rest)
package/server/REST.ts CHANGED
@@ -8,13 +8,17 @@ import { Resources } from '../resources/Resources.ts';
8
8
  import { Resource, missingMethod, allowedMethods } from '../resources/Resource.ts';
9
9
  import { IterableEventQueue } from '../resources/IterableEventQueue.ts';
10
10
  import { transaction } from '../resources/transaction.ts';
11
- import { Headers, mergeHeaders } from '../server/serverHelpers/Headers.ts';
11
+ import { Headers, mergeHeaders, addVaryHeader } from '../server/serverHelpers/Headers.ts';
12
12
  import { generateJsonApi } from '../resources/openApi.ts';
13
13
  import { getConfigPath } from '../config/configUtils.ts';
14
14
  import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
15
15
  import { ASIDE_STAGING_DIR } from '../components/Application.ts';
16
16
  import { COMPONENT_PREPARATION_LOCK_DIR } from '../components/componentPreparationLock.ts';
17
17
  import { restartNeeded } from '../components/requestRestart.ts';
18
+ import {
19
+ assertNoDeferredCredentialRejection,
20
+ settleDeferredCredentialRejection,
21
+ } from '../security/deferredAuthentication.ts';
18
22
 
19
23
  import { Request } from '../server/serverHelpers/Request.ts';
20
24
  import { RequestTarget } from '../resources/RequestTarget';
@@ -117,6 +121,39 @@ async function findInactiveComponent(url: string): Promise<string | undefined> {
117
121
  }
118
122
  }
119
123
 
124
+ /**
125
+ * Emit RFC 7233-style pagination headers for a `Prefer: count=` request. Table.search returns the page
126
+ * with a `recordCount` (total matching records, or null when an exact scan hit its guardrail or an
127
+ * estimate was suppressed by an opaque filter). `Content-Range: items <start>-<end>/<total>` lets a
128
+ * client paginate — `<total>` is `*` when unavailable. `Preference-Applied` echoes the count mode the
129
+ * server actually applied (`exact` or `estimated`, after any per-mount downgrade), so a `.../*` total
130
+ * reads as "that mode was applied but the total is unavailable" rather than "no count was requested".
131
+ * All three headers are added to `Access-Control-Expose-Headers` so a browser can read them cross-origin
132
+ * (they aren't safelisted).
133
+ */
134
+ function setCountHeaders(headers: Headers, offset: number, mode: string, page: any) {
135
+ const total = page.recordCount;
136
+ const len = Array.isArray(page) ? page.length : 0;
137
+ const range = len > 0 ? `${offset}-${offset + len - 1}` : '*';
138
+ const totalStr = typeof total === 'number' ? String(total) : '*';
139
+ headers.set('Range-Unit', 'items');
140
+ headers.set('Content-Range', `items ${range}/${totalStr}`);
141
+ headers.set('Preference-Applied', `count=${mode}`);
142
+ // Append (don't overwrite) so a resource that already exposed its own headers keeps them. Compare
143
+ // case-insensitive comma tokens, not substrings, so an unrelated existing token (e.g.
144
+ // `X-Content-Range-Metadata`) doesn't suppress the real `Content-Range` token.
145
+ const exposed = headers.get('Access-Control-Expose-Headers');
146
+ const existing = new Set(
147
+ (Array.isArray(exposed) ? exposed.join(',') : exposed || '')
148
+ .split(',')
149
+ .map((token) => token.trim().toLowerCase())
150
+ .filter(Boolean)
151
+ );
152
+ for (const name of ['Content-Range', 'Range-Unit', 'Preference-Applied']) {
153
+ if (!existing.has(name.toLowerCase())) headers.append('Access-Control-Expose-Headers', name, true);
154
+ }
155
+ }
156
+
120
157
  async function http(request: Request, nextHandler, resources: Resources, httpOptions: any) {
121
158
  const headersObject = request.headers.asObject;
122
159
  const isSse = headersObject.accept === 'text/event-stream';
@@ -165,7 +202,32 @@ async function http(request: Request, nextHandler, resources: Resources, httpOpt
165
202
 
166
203
  (target as any).async = true;
167
204
  resource = entry.Resource;
205
+ // Pagination total-count opt-in (no default): `Prefer: count=exact|estimated`. Table.search
206
+ // reads target.count to compute the total emitted as Content-Range below. Only honored on
207
+ // GET/HEAD reads: setting it for other methods would hand their `search()` a materialized array
208
+ // instead of the AsyncIterable they iterate (e.g. a collection DELETE at Table.ts).
209
+ const prefer = headersObject['prefer'];
210
+ if (prefer && (method === 'GET' || method === 'HEAD')) {
211
+ // Exact counting scans the full matched set, so it is opt-in per mount via
212
+ // `rest: { exactCount: true }` (default off); count=exact is otherwise served as a cheap
213
+ // estimate. Estimated is always available. Accept a string `"true"` too, since not every
214
+ // config source coerces to a boolean.
215
+ const exactEnabled = (httpOptions as any)?.exactCount === true || (httpOptions as any)?.exactCount === 'true';
216
+ for (const pref of parseHeaderValue(prefer as any)) {
217
+ // The header parser can hand back a non-string value (e.g. a bare `Prefer: count` with no
218
+ // `=`), so coerce before lower-casing rather than calling `.toLowerCase()` on a boolean.
219
+ const mode = String(pref?.value ?? '').toLowerCase();
220
+ if (pref?.name === 'count' && (mode === 'exact' || mode === 'estimated')) {
221
+ // A count=exact request on a mount that hasn't opted in is downgraded to estimated,
222
+ // signaled back to the client via Preference-Applied.
223
+ (target as any).count = mode === 'exact' && !exactEnabled ? 'estimated' : mode;
224
+ break;
225
+ }
226
+ }
227
+ }
168
228
  }
229
+ const settledCredentialRejection = settleDeferredCredentialRejection(request);
230
+ if (settledCredentialRejection) return settledCredentialRejection;
169
231
  if ((resource as any)?.isCaching) {
170
232
  const cacheControl = headersObject['cache-control'];
171
233
  if (cacheControl) {
@@ -348,9 +410,24 @@ async function http(request: Request, nextHandler, resources: Resources, httpOpt
348
410
  }
349
411
  // TODO: Handle 201 Created
350
412
  if (responseData !== undefined) {
413
+ if (
414
+ (target as any)?.count &&
415
+ (method === 'GET' || method === 'HEAD') &&
416
+ Array.isArray(responseData) &&
417
+ (responseData as any).recordCount !== undefined
418
+ ) {
419
+ // Array.isArray guards the single-record path: a record that happens to carry a
420
+ // `recordCount` attribute must not be mistaken for a count page (Table.search only ever
421
+ // returns the count as an array).
422
+ setCountHeaders(headers, (target as any).offset || 0, (target as any).count, responseData);
423
+ }
351
424
  responseObject.body = serialize(responseData, request, responseObject);
352
425
  if (method === 'HEAD') responseObject.body = undefined; // we want everything else to be the same as GET, but then omit the body
353
426
  }
427
+ // A collection read's count headers vary by the request's `Prefer` value; serialize() just reset
428
+ // `Vary`, so declare it here (after serialization) — otherwise a shared cache could serve count
429
+ // headers to a request that didn't ask, or a cached non-count response to one that did.
430
+ if ((method === 'GET' || method === 'HEAD') && (target as any)?.isCollection) addVaryHeader(headers, 'Prefer');
354
431
  return responseObject;
355
432
  } catch (error) {
356
433
  error ??= new Error('Unknown error occurred');
@@ -482,6 +559,7 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
482
559
  // TODO: Ideally we would like to have a 404 response before upgrading to WebSocket protocol, probably
483
560
  return ws.close(1011, `No resource was found to handle ${request.pathname}`);
484
561
  } else {
562
+ assertNoDeferredCredentialRejection(request);
485
563
  request.handlerPath = entry.path;
486
564
  recordAction(
487
565
  (action) => ({
@@ -3,6 +3,7 @@ import type { RequestParams } from 'graphql-http';
3
3
  import { getDeserializer } from './serverHelpers/contentTypes.ts';
4
4
  import { resources } from '../resources/Resources.ts';
5
5
  import logger from '../utility/logging/harper_logger.ts';
6
+ import { settleDeferredCredentialRejection } from '../security/deferredAuthentication.ts';
6
7
 
7
8
  // This code makes heavy use of the word "node" to refer to a node in the GraphQL AST.
8
9
 
@@ -580,6 +581,9 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
580
581
  return nextLayer(request);
581
582
  }
582
583
 
584
+ const settledCredentialRejection = settleDeferredCredentialRejection(request);
585
+ if (settledCredentialRejection) return settledCredentialRejection;
586
+
583
587
  try {
584
588
  // Await the `graphqlHandler` call here so that errors are caught.
585
589
  return await graphqlQueryingHandler(request as any);