harper 5.1.20 → 5.2.0-alpha.5

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 (395) hide show
  1. package/agent/operations.ts +13 -16
  2. package/components/Application.ts +188 -7
  3. package/components/ApplicationScope.ts +3 -0
  4. package/components/Scope.ts +12 -0
  5. package/components/anthropic/index.ts +9 -2
  6. package/components/bedrock/index.ts +25 -2
  7. package/components/componentLoader.ts +56 -7
  8. package/components/componentSecrets.ts +451 -0
  9. package/components/deploymentRecorder.ts +7 -1
  10. package/components/mcp/adapters/fastify.ts +3 -0
  11. package/components/mcp/adapters/harperHttp.ts +3 -0
  12. package/components/mcp/audit.ts +21 -6
  13. package/components/mcp/customResourceRegistry.ts +17 -38
  14. package/components/mcp/listChanged.ts +37 -20
  15. package/components/mcp/quota.ts +145 -0
  16. package/components/mcp/rateLimit.ts +152 -14
  17. package/components/mcp/resources.ts +2 -4
  18. package/components/mcp/toolRegistry.ts +73 -4
  19. package/components/mcp/tools/application.ts +77 -8
  20. package/components/mcp/tools/operations.ts +106 -36
  21. package/components/mcp/tools/schemas/operationDescriptions.ts +4 -4
  22. package/components/mcp/tools/schemas/operations.ts +5 -0
  23. package/components/mcp/transport.ts +55 -4
  24. package/components/ollama/index.ts +6 -2
  25. package/components/openai/index.ts +9 -2
  26. package/components/operations.js +204 -10
  27. package/components/operationsValidation.js +173 -0
  28. package/components/secretOperations.ts +498 -0
  29. package/config/configUtils.ts +25 -1
  30. package/config-app.schema.json +81 -0
  31. package/config-root.schema.json +8 -1
  32. package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  33. package/dataLayer/readAuditLog.ts +14 -0
  34. package/dataLayer/schemaDescribe.ts +1 -0
  35. package/dist/agent/operations.d.ts +7 -4
  36. package/dist/agent/operations.js +13 -15
  37. package/dist/agent/operations.js.map +1 -1
  38. package/dist/components/Application.d.ts +25 -2
  39. package/dist/components/Application.js +166 -7
  40. package/dist/components/Application.js.map +1 -1
  41. package/dist/components/ApplicationScope.d.ts +2 -0
  42. package/dist/components/ApplicationScope.js +3 -0
  43. package/dist/components/ApplicationScope.js.map +1 -1
  44. package/dist/components/Scope.d.ts +8 -0
  45. package/dist/components/Scope.js +11 -0
  46. package/dist/components/Scope.js.map +1 -1
  47. package/dist/components/anthropic/index.d.ts +4 -1
  48. package/dist/components/anthropic/index.js +4 -2
  49. package/dist/components/anthropic/index.js.map +1 -1
  50. package/dist/components/bedrock/index.d.ts +4 -1
  51. package/dist/components/bedrock/index.js +22 -2
  52. package/dist/components/bedrock/index.js.map +1 -1
  53. package/dist/components/componentLoader.d.ts +2 -0
  54. package/dist/components/componentLoader.js +52 -10
  55. package/dist/components/componentLoader.js.map +1 -1
  56. package/dist/components/componentSecrets.d.ts +63 -0
  57. package/dist/components/componentSecrets.js +405 -0
  58. package/dist/components/componentSecrets.js.map +1 -0
  59. package/dist/components/deploymentRecorder.d.ts +3 -0
  60. package/dist/components/deploymentRecorder.js +4 -3
  61. package/dist/components/deploymentRecorder.js.map +1 -1
  62. package/dist/components/mcp/adapters/fastify.d.ts +2 -0
  63. package/dist/components/mcp/adapters/fastify.js +1 -0
  64. package/dist/components/mcp/adapters/fastify.js.map +1 -1
  65. package/dist/components/mcp/adapters/harperHttp.d.ts +2 -0
  66. package/dist/components/mcp/adapters/harperHttp.js +1 -0
  67. package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
  68. package/dist/components/mcp/audit.d.ts +2 -2
  69. package/dist/components/mcp/audit.js +20 -5
  70. package/dist/components/mcp/audit.js.map +1 -1
  71. package/dist/components/mcp/customResourceRegistry.d.ts +5 -12
  72. package/dist/components/mcp/customResourceRegistry.js +15 -30
  73. package/dist/components/mcp/customResourceRegistry.js.map +1 -1
  74. package/dist/components/mcp/listChanged.d.ts +7 -2
  75. package/dist/components/mcp/listChanged.js +28 -0
  76. package/dist/components/mcp/listChanged.js.map +1 -1
  77. package/dist/components/mcp/quota.d.ts +31 -0
  78. package/dist/components/mcp/quota.js +155 -0
  79. package/dist/components/mcp/quota.js.map +1 -0
  80. package/dist/components/mcp/rateLimit.d.ts +19 -3
  81. package/dist/components/mcp/rateLimit.js +129 -13
  82. package/dist/components/mcp/rateLimit.js.map +1 -1
  83. package/dist/components/mcp/resources.js.map +1 -1
  84. package/dist/components/mcp/toolRegistry.d.ts +39 -1
  85. package/dist/components/mcp/toolRegistry.js +60 -5
  86. package/dist/components/mcp/toolRegistry.js.map +1 -1
  87. package/dist/components/mcp/tools/application.d.ts +8 -1
  88. package/dist/components/mcp/tools/application.js +65 -5
  89. package/dist/components/mcp/tools/application.js.map +1 -1
  90. package/dist/components/mcp/tools/operations.d.ts +15 -3
  91. package/dist/components/mcp/tools/operations.js +99 -38
  92. package/dist/components/mcp/tools/operations.js.map +1 -1
  93. package/dist/components/mcp/tools/schemas/operationDescriptions.js +4 -4
  94. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  95. package/dist/components/mcp/tools/schemas/operations.js +4 -0
  96. package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
  97. package/dist/components/mcp/transport.d.ts +6 -0
  98. package/dist/components/mcp/transport.js +47 -3
  99. package/dist/components/mcp/transport.js.map +1 -1
  100. package/dist/components/ollama/index.d.ts +4 -1
  101. package/dist/components/ollama/index.js +4 -2
  102. package/dist/components/ollama/index.js.map +1 -1
  103. package/dist/components/openai/index.d.ts +4 -1
  104. package/dist/components/openai/index.js +4 -2
  105. package/dist/components/openai/index.js.map +1 -1
  106. package/dist/components/operations.d.ts +39 -0
  107. package/dist/components/operations.js +194 -2
  108. package/dist/components/operations.js.map +1 -1
  109. package/dist/components/operationsValidation.d.ts +38 -0
  110. package/dist/components/operationsValidation.js +155 -0
  111. package/dist/components/operationsValidation.js.map +1 -1
  112. package/dist/components/secretOperations.d.ts +113 -0
  113. package/dist/components/secretOperations.js +475 -0
  114. package/dist/components/secretOperations.js.map +1 -0
  115. package/dist/config/configUtils.d.ts +4 -1
  116. package/dist/config/configUtils.js +18 -1
  117. package/dist/config/configUtils.js.map +1 -1
  118. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
  119. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
  120. package/dist/dataLayer/readAuditLog.js +11 -0
  121. package/dist/dataLayer/readAuditLog.js.map +1 -1
  122. package/dist/dataLayer/schemaDescribe.js +2 -0
  123. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  124. package/dist/globals.d.ts +1 -1
  125. package/dist/globals.js +1 -0
  126. package/dist/globals.js.map +1 -1
  127. package/dist/index.d.ts +3 -0
  128. package/dist/index.js +1 -0
  129. package/dist/index.js.map +1 -1
  130. package/dist/json/systemSchema.json +38 -0
  131. package/dist/resources/DatabaseTransaction.d.ts +31 -3
  132. package/dist/resources/DatabaseTransaction.js +190 -29
  133. package/dist/resources/DatabaseTransaction.js.map +1 -1
  134. package/dist/resources/LMDBTransaction.d.ts +1 -1
  135. package/dist/resources/LMDBTransaction.js +43 -5
  136. package/dist/resources/LMDBTransaction.js.map +1 -1
  137. package/dist/resources/PrimaryRocksDatabase.d.ts +51 -0
  138. package/dist/resources/PrimaryRocksDatabase.js +194 -0
  139. package/dist/resources/PrimaryRocksDatabase.js.map +1 -0
  140. package/dist/resources/RecordEncoder.d.ts +1 -0
  141. package/dist/resources/RecordEncoder.js +81 -75
  142. package/dist/resources/RecordEncoder.js.map +1 -1
  143. package/dist/resources/RequestTarget.d.ts +19 -0
  144. package/dist/resources/RequestTarget.js.map +1 -1
  145. package/dist/resources/Resource.d.ts +0 -1
  146. package/dist/resources/Resource.js +103 -16
  147. package/dist/resources/Resource.js.map +1 -1
  148. package/dist/resources/ResourceInterface.d.ts +6 -2
  149. package/dist/resources/ResourceInterface.js.map +1 -1
  150. package/dist/resources/Resources.js.map +1 -1
  151. package/dist/resources/Table.d.ts +6 -4
  152. package/dist/resources/Table.js +247 -24
  153. package/dist/resources/Table.js.map +1 -1
  154. package/dist/resources/analytics/read.js +25 -28
  155. package/dist/resources/analytics/read.js.map +1 -1
  156. package/dist/resources/blob.js +165 -22
  157. package/dist/resources/blob.js.map +1 -1
  158. package/dist/resources/crdt.d.ts +2 -0
  159. package/dist/resources/crdt.js +45 -36
  160. package/dist/resources/crdt.js.map +1 -1
  161. package/dist/resources/databases.d.ts +1 -0
  162. package/dist/resources/databases.js +134 -7
  163. package/dist/resources/databases.js.map +1 -1
  164. package/dist/resources/graphql.js +8 -0
  165. package/dist/resources/graphql.js.map +1 -1
  166. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +35 -5
  167. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +239 -14
  168. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  169. package/dist/resources/jsResource.js +6 -0
  170. package/dist/resources/jsResource.js.map +1 -1
  171. package/dist/resources/loadEnv.js +30 -3
  172. package/dist/resources/loadEnv.js.map +1 -1
  173. package/dist/resources/login.js +2 -2
  174. package/dist/resources/login.js.map +1 -1
  175. package/dist/resources/models/Models.js +14 -3
  176. package/dist/resources/models/Models.js.map +1 -1
  177. package/dist/resources/models/embedHook.js +10 -2
  178. package/dist/resources/models/embedHook.js.map +1 -1
  179. package/dist/resources/models/openaiStream.d.ts +56 -0
  180. package/dist/resources/models/openaiStream.js +94 -0
  181. package/dist/resources/models/openaiStream.js.map +1 -0
  182. package/dist/resources/replayLogs.js +4 -3
  183. package/dist/resources/replayLogs.js.map +1 -1
  184. package/dist/resources/search.d.ts +1 -1
  185. package/dist/resources/search.js +104 -11
  186. package/dist/resources/search.js.map +1 -1
  187. package/dist/resources/secretDecryptor.d.ts +54 -0
  188. package/dist/resources/secretDecryptor.js +131 -0
  189. package/dist/resources/secretDecryptor.js.map +1 -0
  190. package/dist/resources/tracked.js +15 -5
  191. package/dist/resources/tracked.js.map +1 -1
  192. package/dist/security/auth.js +71 -8
  193. package/dist/security/auth.js.map +1 -1
  194. package/dist/security/jsLoader.js +11 -0
  195. package/dist/security/jsLoader.js.map +1 -1
  196. package/dist/security/tokenAuthentication.d.ts +9 -1
  197. package/dist/security/tokenAuthentication.js +31 -1
  198. package/dist/security/tokenAuthentication.js.map +1 -1
  199. package/dist/server/DurableSubscriptionsSession.d.ts +1 -1
  200. package/dist/server/DurableSubscriptionsSession.js +8 -4
  201. package/dist/server/DurableSubscriptionsSession.js.map +1 -1
  202. package/dist/server/REST.js +28 -9
  203. package/dist/server/REST.js.map +1 -1
  204. package/dist/server/fastifyRoutes.js +11 -1
  205. package/dist/server/fastifyRoutes.js.map +1 -1
  206. package/dist/server/graphqlQuerying.js +4 -3
  207. package/dist/server/graphqlQuerying.js.map +1 -1
  208. package/dist/server/http.d.ts +40 -2
  209. package/dist/server/http.js +560 -77
  210. package/dist/server/http.js.map +1 -1
  211. package/dist/server/itc/serverHandlers.js +64 -0
  212. package/dist/server/itc/serverHandlers.js.map +1 -1
  213. package/dist/server/liveSubscriptionAuth.d.ts +12 -0
  214. package/dist/server/liveSubscriptionAuth.js +140 -0
  215. package/dist/server/liveSubscriptionAuth.js.map +1 -0
  216. package/dist/server/loadRootComponents.js +2 -1
  217. package/dist/server/loadRootComponents.js.map +1 -1
  218. package/dist/server/middlewareChain.d.ts +44 -1
  219. package/dist/server/middlewareChain.js +86 -10
  220. package/dist/server/middlewareChain.js.map +1 -1
  221. package/dist/server/mqtt.js +2 -1
  222. package/dist/server/mqtt.js.map +1 -1
  223. package/dist/server/operationsServer.js +1 -1
  224. package/dist/server/operationsServer.js.map +1 -1
  225. package/dist/server/serverHelpers/Headers.d.ts +9 -0
  226. package/dist/server/serverHelpers/Headers.js +22 -0
  227. package/dist/server/serverHelpers/Headers.js.map +1 -1
  228. package/dist/server/serverHelpers/JSONStream.js +3 -3
  229. package/dist/server/serverHelpers/JSONStream.js.map +1 -1
  230. package/dist/server/serverHelpers/Request.d.ts +52 -1
  231. package/dist/server/serverHelpers/Request.js +122 -1
  232. package/dist/server/serverHelpers/Request.js.map +1 -1
  233. package/dist/server/serverHelpers/contentTypes.js +3 -8
  234. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  235. package/dist/server/serverHelpers/progressEmitter.d.ts +19 -0
  236. package/dist/server/serverHelpers/progressEmitter.js +52 -4
  237. package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
  238. package/dist/server/serverHelpers/registeredOperations.d.ts +40 -0
  239. package/dist/server/serverHelpers/registeredOperations.js +286 -0
  240. package/dist/server/serverHelpers/registeredOperations.js.map +1 -0
  241. package/dist/server/serverHelpers/serverHandlers.js +11 -5
  242. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  243. package/dist/server/serverHelpers/serverUtilities.d.ts +1 -0
  244. package/dist/server/serverHelpers/serverUtilities.js +67 -5
  245. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  246. package/dist/server/serverHelpers/uwsServer.d.ts +80 -0
  247. package/dist/server/serverHelpers/uwsServer.js +433 -0
  248. package/dist/server/serverHelpers/uwsServer.js.map +1 -0
  249. package/dist/server/static.d.ts +0 -15
  250. package/dist/server/static.js +161 -2
  251. package/dist/server/static.js.map +1 -1
  252. package/dist/server/status/index.d.ts +4 -1
  253. package/dist/server/status/index.js +64 -4
  254. package/dist/server/status/index.js.map +1 -1
  255. package/dist/server/threads/manageThreads.d.ts +10 -0
  256. package/dist/server/threads/manageThreads.js +107 -4
  257. package/dist/server/threads/manageThreads.js.map +1 -1
  258. package/dist/server/threads/socketRouter.js +5 -1
  259. package/dist/server/threads/socketRouter.js.map +1 -1
  260. package/dist/server/threads/threadServer.js +92 -14
  261. package/dist/server/threads/threadServer.js.map +1 -1
  262. package/dist/upgrade/directives/5-2-0.d.ts +7 -0
  263. package/dist/upgrade/directives/5-2-0.js +108 -0
  264. package/dist/upgrade/directives/5-2-0.js.map +1 -0
  265. package/dist/upgrade/directives/directivesController.js +2 -1
  266. package/dist/upgrade/directives/directivesController.js.map +1 -1
  267. package/dist/utility/common_utils.js +5 -0
  268. package/dist/utility/common_utils.js.map +1 -1
  269. package/dist/utility/envFile.d.ts +41 -0
  270. package/dist/utility/envFile.js +221 -0
  271. package/dist/utility/envFile.js.map +1 -0
  272. package/dist/utility/globalSchema.d.ts +9 -0
  273. package/dist/utility/hdbTerms.d.ts +25 -0
  274. package/dist/utility/hdbTerms.js +31 -0
  275. package/dist/utility/hdbTerms.js.map +1 -1
  276. package/dist/utility/logging/harper_logger.d.ts +21 -0
  277. package/dist/utility/logging/harper_logger.js +160 -8
  278. package/dist/utility/logging/harper_logger.js.map +1 -1
  279. package/dist/utility/logging/logRotator.js +29 -17
  280. package/dist/utility/logging/logRotator.js.map +1 -1
  281. package/dist/utility/logging/readLog.d.ts +1 -1
  282. package/dist/utility/logging/readLog.js +305 -2
  283. package/dist/utility/logging/readLog.js.map +1 -1
  284. package/dist/utility/operationPermissions.d.ts +18 -0
  285. package/dist/utility/operationPermissions.js +35 -1
  286. package/dist/utility/operationPermissions.js.map +1 -1
  287. package/dist/utility/operation_authorization.d.ts +17 -0
  288. package/dist/utility/operation_authorization.js +38 -0
  289. package/dist/utility/operation_authorization.js.map +1 -1
  290. package/dist/utility/secretEnvelope.d.ts +30 -0
  291. package/dist/utility/secretEnvelope.js +94 -0
  292. package/dist/utility/secretEnvelope.js.map +1 -0
  293. package/dist/utility/signalling.d.ts +7 -0
  294. package/dist/utility/signalling.js +16 -0
  295. package/dist/utility/signalling.js.map +1 -1
  296. package/dist/validation/configValidator.js +70 -24
  297. package/dist/validation/configValidator.js.map +1 -1
  298. package/index.ts +4 -0
  299. package/json/systemSchema.json +38 -0
  300. package/npm-shrinkwrap.json +15066 -9347
  301. package/package.json +13 -8
  302. package/resources/DESIGN.md +15 -12
  303. package/resources/DatabaseTransaction.ts +206 -37
  304. package/resources/LMDBTransaction.ts +43 -9
  305. package/resources/PrimaryRocksDatabase.ts +201 -0
  306. package/resources/RecordEncoder.ts +81 -69
  307. package/resources/RequestTarget.ts +21 -0
  308. package/resources/Resource.ts +109 -25
  309. package/resources/ResourceInterface.ts +6 -2
  310. package/resources/Resources.ts +1 -3
  311. package/resources/Table.ts +238 -29
  312. package/resources/analytics/read.ts +25 -30
  313. package/resources/blob.ts +159 -23
  314. package/resources/crdt.ts +37 -31
  315. package/resources/databases.ts +143 -6
  316. package/resources/graphql.ts +8 -0
  317. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +251 -17
  318. package/resources/jsResource.ts +6 -0
  319. package/resources/loadEnv.ts +34 -3
  320. package/resources/login.ts +2 -2
  321. package/resources/models/Models.ts +19 -3
  322. package/resources/models/embedHook.ts +13 -2
  323. package/resources/models/openaiStream.ts +133 -0
  324. package/resources/replayLogs.ts +4 -3
  325. package/resources/search.ts +113 -11
  326. package/resources/secretDecryptor.ts +159 -0
  327. package/resources/tracked.ts +24 -17
  328. package/security/auth.ts +70 -10
  329. package/security/jsLoader.ts +12 -0
  330. package/security/tokenAuthentication.ts +42 -1
  331. package/server/DESIGN.md +10 -0
  332. package/server/DurableSubscriptionsSession.ts +8 -4
  333. package/server/REST.ts +26 -9
  334. package/server/fastifyRoutes.ts +10 -1
  335. package/server/graphqlQuerying.ts +4 -3
  336. package/server/http.ts +563 -77
  337. package/server/itc/serverHandlers.js +70 -0
  338. package/server/liveSubscriptionAuth.ts +152 -0
  339. package/server/loadRootComponents.js +2 -1
  340. package/server/middlewareChain.ts +107 -17
  341. package/server/mqtt.ts +2 -1
  342. package/server/operationsServer.ts +2 -2
  343. package/server/serverHelpers/Headers.ts +24 -0
  344. package/server/serverHelpers/JSONStream.ts +3 -3
  345. package/server/serverHelpers/Request.ts +127 -0
  346. package/server/serverHelpers/contentTypes.ts +3 -8
  347. package/server/serverHelpers/progressEmitter.ts +55 -5
  348. package/server/serverHelpers/registeredOperations.ts +259 -0
  349. package/server/serverHelpers/serverHandlers.js +11 -5
  350. package/server/serverHelpers/serverUtilities.ts +88 -5
  351. package/server/serverHelpers/uwsServer.ts +488 -0
  352. package/server/static.ts +180 -2
  353. package/server/status/index.ts +72 -4
  354. package/server/threads/manageThreads.js +102 -4
  355. package/server/threads/socketRouter.ts +5 -1
  356. package/server/threads/threadServer.js +93 -10
  357. package/studio/web/assets/{Chat-SdD1EZca.js → Chat-Cv_2paZE.js} +2 -2
  358. package/studio/web/assets/{Chat-SdD1EZca.js.map → Chat-Cv_2paZE.js.map} +1 -1
  359. package/studio/web/assets/{FloatingChat-GMq6tHK9.js → FloatingChat-CPyPIcVR.js} +4 -4
  360. package/studio/web/assets/{FloatingChat-GMq6tHK9.js.map → FloatingChat-CPyPIcVR.js.map} +1 -1
  361. package/studio/web/assets/{applications-B5hhv7uA.js → applications-C0jT2vdZ.js} +2 -2
  362. package/studio/web/assets/{applications-B5hhv7uA.js.map → applications-C0jT2vdZ.js.map} +1 -1
  363. package/studio/web/assets/{index-VFcpNWgq.js → index-D_GKOkhn.js} +6 -6
  364. package/studio/web/assets/index-D_GKOkhn.js.map +1 -0
  365. package/studio/web/assets/{index.lazy-DgcJojIA.js → index.lazy-CAmCIA65.js} +4 -4
  366. package/studio/web/assets/{index.lazy-DgcJojIA.js.map → index.lazy-CAmCIA65.js.map} +1 -1
  367. package/studio/web/assets/{profile-BqN-8vzm.js → profile-CaF-4aZe.js} +2 -2
  368. package/studio/web/assets/{profile-BqN-8vzm.js.map → profile-CaF-4aZe.js.map} +1 -1
  369. package/studio/web/assets/{setComponentFile-CHFvFq9u.js → setComponentFile-DLW1DHCt.js} +2 -2
  370. package/studio/web/assets/{setComponentFile-CHFvFq9u.js.map → setComponentFile-DLW1DHCt.js.map} +1 -1
  371. package/studio/web/assets/{setup-CQ3vWJSr.js → setup-TGCErIhq.js} +2 -2
  372. package/studio/web/assets/{setup-CQ3vWJSr.js.map → setup-TGCErIhq.js.map} +1 -1
  373. package/studio/web/assets/{status-BsW5fjuh.js → status-DG0Maoao.js} +2 -2
  374. package/studio/web/assets/{status-BsW5fjuh.js.map → status-DG0Maoao.js.map} +1 -1
  375. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js → swagger-ui-react-8T4SgQ1m.js} +2 -2
  376. package/studio/web/assets/{swagger-ui-react-DITdeB9-.js.map → swagger-ui-react-8T4SgQ1m.js.map} +1 -1
  377. package/studio/web/assets/{tsMode-Dyph-OUs.js → tsMode-DJ6Sl24Q.js} +2 -2
  378. package/studio/web/assets/{tsMode-Dyph-OUs.js.map → tsMode-DJ6Sl24Q.js.map} +1 -1
  379. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js → useEntityRestURL-JO2mfWTQ.js} +2 -2
  380. package/studio/web/assets/{useEntityRestURL-D0QTUqex.js.map → useEntityRestURL-JO2mfWTQ.js.map} +1 -1
  381. package/studio/web/index.html +1 -1
  382. package/upgrade/directives/5-2-0.ts +82 -0
  383. package/upgrade/directives/directivesController.ts +2 -1
  384. package/utility/common_utils.ts +5 -0
  385. package/utility/envFile.ts +218 -0
  386. package/utility/hdbTerms.ts +31 -0
  387. package/utility/logging/harper_logger.ts +156 -8
  388. package/utility/logging/logRotator.ts +24 -14
  389. package/utility/logging/readLog.ts +323 -2
  390. package/utility/operationPermissions.ts +35 -1
  391. package/utility/operation_authorization.ts +74 -1
  392. package/utility/secretEnvelope.ts +113 -0
  393. package/utility/signalling.ts +15 -0
  394. package/validation/configValidator.ts +78 -25
  395. package/studio/web/assets/index-VFcpNWgq.js.map +0 -1
@@ -0,0 +1,498 @@
1
+ 'use strict';
2
+
3
+ // Operations against system.hdb_secret — the envelope-encrypted secrets store (#715).
4
+ //
5
+ // Only ciphertext is ever stored: a plaintext `value` submitted to set_secret is encrypted
6
+ // immediately with the cluster public key (custody must be registered) and the plaintext is
7
+ // discarded; processLocalTransaction strips `value`/`envelope` from the operations log before
8
+ // dispatch. Rows reach peers via normal system-table replication — these operations deliberately
9
+ // do NOT call replicateOperation (op replication on top of table replication would double-apply).
10
+ //
11
+ // All operations are super_user only, enforced in-handler in addition to the requiredPermissions
12
+ // registration: an `operations` allowlist on a role can otherwise delegate SU-only ops (see the
13
+ // gate-2 bypass in utility/operation_authorization.ts), and secret custody must not be delegable.
14
+
15
+ import { existsSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+ import { databases } from '../resources/databases.ts';
18
+ import * as configUtils from '../config/configUtils.ts';
19
+ import * as terms from '../utility/hdbTerms.ts';
20
+ import { ClientError, handleHDBError, hdbErrors } from '../utility/errors/hdbError.ts';
21
+ import logger from '../utility/logging/harper_logger.ts';
22
+ import * as validator from './operationsValidation.js';
23
+ import { getSecretCustody } from '../resources/secretDecryptor.ts';
24
+ import { encryptEnvelope, parseEnvelopeFields } from '../utility/secretEnvelope.ts';
25
+ import { ENV_ENCRYPTED_PREFIX } from '../utility/envFile.ts';
26
+
27
+ const { HTTP_STATUS_CODES } = hdbErrors;
28
+ const SECRET_TABLE = terms.SYSTEM_TABLE_NAMES.SECRET_TABLE_NAME;
29
+
30
+ function requireSuperUser(req: any): void {
31
+ if (!req?.hdb_user?.role?.permission?.super_user) {
32
+ throw new ClientError(
33
+ `Operation '${req?.operation}' is restricted to super_user roles`,
34
+ HTTP_STATUS_CODES.FORBIDDEN
35
+ );
36
+ }
37
+ }
38
+
39
+ function validate(validation: any): void {
40
+ if (validation) {
41
+ throw handleHDBError(validation, validation.message, HTTP_STATUS_CODES.BAD_REQUEST);
42
+ }
43
+ }
44
+
45
+ function secretTable() {
46
+ const table = (databases as any).system?.[SECRET_TABLE];
47
+ if (!table) {
48
+ throw new ClientError(
49
+ `The secrets store is not initialized on this node (system.${SECRET_TABLE} missing). ` +
50
+ `Run upgrade or restart the server to provision the table.`
51
+ );
52
+ }
53
+ return table;
54
+ }
55
+
56
+ // Grants are a set: duplicates would let one revoke leave a residual grant behind, so every write
57
+ // path normalizes through this (including rows that arrived dirty via replication).
58
+ function dedupeGrants(grants: any): string[] {
59
+ return Array.isArray(grants) ? [...new Set(grants)] : [];
60
+ }
61
+
62
+ // Rebuild a plain record from a stored row's known attributes (never spread rows — RecordObject
63
+ // prototype fields don't survive a spread reliably; see DESIGN.md).
64
+ function toRecord(row: any) {
65
+ return {
66
+ name: row.name,
67
+ envelope: row.envelope,
68
+ kid: row.kid ?? null,
69
+ grants: dedupeGrants(row.grants),
70
+ processEnv: !!row.processEnv,
71
+ metadata: row.metadata ?? {},
72
+ unverified: !!row.unverified,
73
+ updated_by: row.updated_by ?? null,
74
+ };
75
+ }
76
+
77
+ // True when a directory with the component's name exists under the components root. Advisory
78
+ // only (audit breadcrumb) — must never block or throw.
79
+ function componentIsKnown(component: string): boolean {
80
+ try {
81
+ return existsSync(join(configUtils.getConfigPath(terms.CONFIG_PARAMS.COMPONENTSROOT), component));
82
+ } catch {
83
+ return false;
84
+ }
85
+ }
86
+
87
+ // Serialize read-modify-write mutations per secret name. The operations API dispatches these
88
+ // handlers on the MAIN thread only, so an in-process promise chain is sufficient to make the
89
+ // get→mutate→put of set_secret/grant_secret/revoke_secret/delete_secret atomic against local
90
+ // concurrency (without it, overlapping grant/revoke calls can silently drop each other's
91
+ // mutation). Cross-node concurrency remains last-write-wins on the whole row — the known
92
+ // system-table replication semantic.
93
+ const nameLocks = new Map<string, Promise<void>>();
94
+ async function withSecretLock<T>(name: string, fn: () => Promise<T>): Promise<T> {
95
+ const prior = nameLocks.get(name) ?? Promise.resolve();
96
+ const run = prior.then(fn);
97
+ // The stored tail never rejects, so a failed mutation doesn't poison the chain.
98
+ const tail = run.then(
99
+ () => undefined,
100
+ () => undefined
101
+ );
102
+ nameLocks.set(name, tail);
103
+ try {
104
+ return await run;
105
+ } finally {
106
+ if (nameLocks.get(name) === tail) nameLocks.delete(name);
107
+ }
108
+ }
109
+
110
+ // Structured audit event for every mutation — operation, secret name, and user, never values.
111
+ function notifyMutation(operation: string, name: string, req: any, detail?: string) {
112
+ logger.notify(
113
+ `secret mutation: op=${operation} name=${name} user=${req?.hdb_user?.username ?? 'unknown'}${detail ? ` ${detail}` : ''}`
114
+ );
115
+ }
116
+
117
+ /**
118
+ * Upsert a secret. Exactly one of `value` (plaintext, encrypted immediately — requires custody)
119
+ * or `envelope` (`enc:v1:...` ciphertext, encrypted client-side against get_secrets_public_key).
120
+ * `kid` is derived from the envelope server-side; a client-supplied kid field is never trusted.
121
+ * Never returns or logs plaintext.
122
+ *
123
+ * The delivery tier is explicit and mutually exclusive (a process-env secret is already global, so
124
+ * scoping it is meaningless):
125
+ * - `processEnv: true` — materialized into the real `process.env` at component load, inherited by
126
+ * child processes (`.env` semantics). No isolation promise.
127
+ * - `grants: [...]` — scoped: never in `process.env`, exposed only to the listed components via the
128
+ * `secrets` accessor.
129
+ * Both default to the stored row so a value rotation preserves the tier without re-specifying it.
130
+ */
131
+ export async function setSecret(req: any) {
132
+ requireSuperUser(req);
133
+ validate(validator.setSecretValidator(req));
134
+
135
+ const table = secretTable();
136
+ const { name } = req;
137
+ const custody = getSecretCustody();
138
+ let envelope: string;
139
+ let kid: string | null;
140
+ let unverified = false;
141
+
142
+ if (req.value !== undefined) {
143
+ if (!custody) {
144
+ throw new ClientError('secrets custody is not initialized on this node');
145
+ }
146
+ const { publicKey, fingerprint } = custody.getPublicKey();
147
+ envelope = ENV_ENCRYPTED_PREFIX + encryptEnvelope(req.value, publicKey, fingerprint);
148
+ kid = fingerprint;
149
+ delete req.value; // plaintext is now sealed; drop the reference
150
+ } else {
151
+ // Deliberately NEVER decrypted server-side: a decrypt-verify would put the plaintext in
152
+ // this thread's heap, defeating client-side encryption. Ingest checks are structural
153
+ // (marker, base64url JSON, k/iv/ct/tag base64 fields) plus a kid-vs-custody fingerprint
154
+ // match; a tampered-but-well-formed envelope is only caught at consumption time.
155
+ envelope = req.envelope;
156
+ if (!envelope.startsWith(ENV_ENCRYPTED_PREFIX)) {
157
+ throw new ClientError(`'envelope' must start with '${ENV_ENCRYPTED_PREFIX}'`);
158
+ }
159
+ let fields;
160
+ try {
161
+ fields = parseEnvelopeFields(envelope.slice(ENV_ENCRYPTED_PREFIX.length));
162
+ } catch (error) {
163
+ throw new ClientError(`Invalid secret envelope: ${(error as Error).message}`);
164
+ }
165
+ kid = fields.kid ?? null;
166
+ if (custody) {
167
+ const { fingerprint } = custody.getPublicKey();
168
+ if (kid !== null && kid !== fingerprint) {
169
+ throw new ClientError(
170
+ `Secret envelope kid '${kid}' does not match this cluster's secrets key ` +
171
+ `(expected '${fingerprint}'; fetch it via get_secrets_public_key)`
172
+ );
173
+ }
174
+ if (kid === null) {
175
+ // No sealed kid to check against custody — accept, but surface it in list_secrets.
176
+ unverified = true;
177
+ logger.warn(`set_secret '${name}': envelope carries no kid; accepted without key-identity verification`);
178
+ }
179
+ } else {
180
+ // No custody on this node: accept, but mark the row so a bad envelope is visible in
181
+ // list_secrets instead of silently failing later.
182
+ unverified = true;
183
+ logger.warn(
184
+ `set_secret '${name}': envelope accepted without verification (no secrets custody registered on this node)`
185
+ );
186
+ }
187
+ }
188
+
189
+ return withSecretLock(name, async () => {
190
+ const existing = await table.get(name);
191
+ const processEnv = req.processEnv ?? existing?.processEnv ?? false;
192
+ const grants = dedupeGrants(req.grants ?? existing?.grants);
193
+ if (processEnv && grants.length > 0) {
194
+ throw new ClientError(
195
+ `Secret '${name}' cannot be both processEnv and grant-scoped — a processEnv secret is global. ` +
196
+ `Set processEnv:false to scope it with grants, or omit grants.`
197
+ );
198
+ }
199
+ await table.put({
200
+ name,
201
+ envelope,
202
+ kid,
203
+ grants: processEnv ? [] : grants,
204
+ processEnv,
205
+ metadata: req.metadata ?? existing?.metadata ?? {},
206
+ unverified,
207
+ updated_by: req.hdb_user?.username ?? null,
208
+ });
209
+ notifyMutation(terms.OPERATIONS_ENUM.SET_SECRET, name, req, unverified ? 'unverified=true' : undefined);
210
+ return { name, kid, created: existing == null };
211
+ });
212
+ }
213
+
214
+ async function mutateGrants(req: any, grant: boolean) {
215
+ requireSuperUser(req);
216
+ validate(validator.grantSecretValidator(req));
217
+
218
+ const table = secretTable();
219
+ return withSecretLock(req.name, async () => {
220
+ const row = await table.get(req.name);
221
+ if (!row) {
222
+ throw new ClientError(`No secret found with name '${req.name}'`, HTTP_STATUS_CODES.NOT_FOUND);
223
+ }
224
+ const record = toRecord(row); // record.grants comes back deduped
225
+ // A processEnv secret is global; granting/revoking a component scope is contradictory. Revoke
226
+ // is a no-op on an already-empty grants list, so only the grant path needs to reject loudly.
227
+ if (grant && record.processEnv) {
228
+ throw new ClientError(
229
+ `Secret '${req.name}' is a processEnv (global) secret and cannot be scoped to a component. ` +
230
+ `Re-run set_secret with processEnv:false to convert it to a scoped secret first.`
231
+ );
232
+ }
233
+ const present = record.grants.includes(req.component);
234
+ const storedLength = Array.isArray(row.grants) ? row.grants.length : 0;
235
+ // No-op short-circuit: nothing to write, nothing to audit, no updated_by/__updatedtime__
236
+ // bump. A stored row with duplicate grants (dirty state) is NOT a no-op — falling through
237
+ // persists the normalized set.
238
+ if ((grant ? present : !present) && record.grants.length === storedLength) {
239
+ return { name: req.name, grants: record.grants, changed: false };
240
+ }
241
+ if (grant && !present) record.grants.push(req.component);
242
+ // Remove every occurrence — with the dedupe this is belt-and-braces against dirty rows.
243
+ if (!grant) record.grants = record.grants.filter((component) => component !== req.component);
244
+ record.updated_by = req.hdb_user?.username ?? null;
245
+ await table.put(record);
246
+ // Granting to a not-yet-deployed component is legal (grants may precede the deploy), but an
247
+ // unknown name is worth a breadcrumb in the audit event — it is usually a typo.
248
+ let detail = `component=${req.component}`;
249
+ if (grant && !componentIsKnown(req.component)) detail += ' component_known=false';
250
+ notifyMutation(
251
+ grant ? terms.OPERATIONS_ENUM.GRANT_SECRET : terms.OPERATIONS_ENUM.REVOKE_SECRET,
252
+ req.name,
253
+ req,
254
+ detail
255
+ );
256
+ return { name: req.name, grants: record.grants, changed: true };
257
+ });
258
+ }
259
+
260
+ /** Add a component to a secret's grants list (idempotent). */
261
+ export function grantSecret(req: any) {
262
+ return mutateGrants(req, true);
263
+ }
264
+
265
+ /** Remove a component from a secret's grants list (idempotent). */
266
+ export function revokeSecret(req: any) {
267
+ return mutateGrants(req, false);
268
+ }
269
+
270
+ /**
271
+ * List secret metadata: never envelopes, never values. Includes the node's current custody
272
+ * fingerprint (null when custody is absent) and a per-row kid_matches_custody flag so stale rows
273
+ * on a cloned/rekeyed node are immediately visible.
274
+ */
275
+ export async function listSecrets(req: any) {
276
+ requireSuperUser(req);
277
+
278
+ const table = secretTable();
279
+ const custody = getSecretCustody();
280
+ const custodyFingerprint = custody ? custody.getPublicKey().fingerprint : null;
281
+ const secrets: any[] = [];
282
+ for await (const row of table.search([])) {
283
+ secrets.push({
284
+ name: row.name,
285
+ kid: row.kid ?? null,
286
+ grants: Array.isArray(row.grants) ? [...row.grants] : [],
287
+ processEnv: !!row.processEnv,
288
+ metadata: row.metadata ?? {},
289
+ unverified: !!row.unverified,
290
+ updated_by: row.updated_by ?? null,
291
+ __createdtime__: row.__createdtime__,
292
+ __updatedtime__: row.__updatedtime__,
293
+ kid_matches_custody: custodyFingerprint !== null && row.kid === custodyFingerprint,
294
+ });
295
+ }
296
+ secrets.sort((a, b) => String(a.name).localeCompare(String(b.name)));
297
+ return { secrets, custody_fingerprint: custodyFingerprint };
298
+ }
299
+
300
+ /** Delete a secret row. Not cryptographic erasure — audit/txn logs and backups retain envelopes. */
301
+ export async function deleteSecret(req: any) {
302
+ requireSuperUser(req);
303
+ validate(validator.deleteSecretValidator(req));
304
+
305
+ const table = secretTable();
306
+ return withSecretLock(req.name, async () => {
307
+ const row = await table.get(req.name);
308
+ if (!row) {
309
+ throw new ClientError(`No secret found with name '${req.name}'`, HTTP_STATUS_CODES.NOT_FOUND);
310
+ }
311
+ await table.delete(req.name);
312
+ notifyMutation(terms.OPERATIONS_ENUM.DELETE_SECRET, req.name, req);
313
+ return { message: `Successfully deleted secret '${req.name}'` };
314
+ });
315
+ }
316
+
317
+ /** The cluster secrets public key, for client-side envelope encryption. */
318
+ export function getSecretsPublicKey(req: any) {
319
+ requireSuperUser(req);
320
+ const custody = getSecretCustody();
321
+ if (!custody) {
322
+ throw new ClientError('secrets custody is not initialized on this node');
323
+ }
324
+ const { publicKey, fingerprint } = custody.getPublicKey();
325
+ return { public_key: publicKey, fingerprint };
326
+ }
327
+
328
+ /** A resolved registry-auth entry as consumed by the transient .npmrc writer. */
329
+ export interface ResolvedRegistryAuthEntry {
330
+ registry: string;
331
+ token: string;
332
+ scope?: string;
333
+ }
334
+
335
+ /** A registry-auth entry after ingestion: a reference into hdb_secret, never a token. */
336
+ export interface RegistryAuthReference {
337
+ registry: string;
338
+ secret: string;
339
+ scope?: string;
340
+ }
341
+
342
+ /**
343
+ * Deterministic name for the auto-minted secret backing a literal registry token: keyed by the
344
+ * deploying component and the registry, so re-supplying (or rotating) the token on a later deploy
345
+ * overwrites the same row rather than accumulating one per deploy. Sanitized to the set_secret name
346
+ * grammar (`\w.-`), since a registry can carry a scheme, port, or path.
347
+ */
348
+ export function deriveRegistrySecretName(component: string, registry: string): string {
349
+ const registryKey = registry
350
+ .trim()
351
+ .replace(/^https?:\/\//i, '')
352
+ .replace(/^\/\//, '')
353
+ .replace(/\/+$/, '')
354
+ .toLowerCase()
355
+ .replace(/[^\w.-]+/g, '_');
356
+ const componentKey = String(component).replace(/[^\w.-]+/g, '_');
357
+ return `deploy.${componentKey}.${registryKey}`;
358
+ }
359
+
360
+ /**
361
+ * Ingest deploy_component `registryAuth` into the secrets store so a provided registry token lives
362
+ * as ciphertext in the replicated, audited `hdb_secret` store (reference, not embed) rather than
363
+ * travelling in the operation body. Returns the entries in reference form (`{ registry, secret }`).
364
+ *
365
+ * - A literal `{ registry, token }` entry is encrypted (via `set_secret`, custody required) under a
366
+ * derived name granted to `component`, and returned as a reference. Overwrites an existing
367
+ * derived row so token rotation is idempotent.
368
+ * - An already-reference `{ registry, secret }` entry passes through unchanged (peers re-running a
369
+ * replicated deploy already carry references — they never re-ingest).
370
+ * - With no custody on this node (OSS core, or key not held here), literal tokens CANNOT be sealed;
371
+ * they pass through untouched so the caller can fall back to the transient, this-node-only path.
372
+ * The reference-form result therefore contains only entries the caller may persist and replicate.
373
+ *
374
+ * Runs on the deploying main thread where custody is registered.
375
+ */
376
+ export async function ingestRegistryAuth(req: any, registryAuth: any[] | undefined, component: string): Promise<any[]> {
377
+ if (!Array.isArray(registryAuth) || registryAuth.length === 0) return registryAuth ?? [];
378
+ const custody = getSecretCustody();
379
+ const out: any[] = [];
380
+ for (const entry of registryAuth) {
381
+ // Already a reference (or nothing to seal without custody): leave as-is.
382
+ if (entry.secret !== undefined || !custody) {
383
+ out.push(entry);
384
+ continue;
385
+ }
386
+ const name = deriveRegistrySecretName(component, entry.registry);
387
+ // Reuse set_secret's seal-and-store path (encrypt with custody, grant to the component, audit
388
+ // the mutation). The deploy request is already super_user, which set_secret requires.
389
+ await setSecret({
390
+ operation: terms.OPERATIONS_ENUM.SET_SECRET,
391
+ hdb_user: req?.hdb_user,
392
+ name,
393
+ value: entry.token,
394
+ grants: [component],
395
+ processEnv: false,
396
+ });
397
+ out.push(
398
+ entry.scope === undefined
399
+ ? { registry: entry.registry, secret: name }
400
+ : { registry: entry.registry, secret: name, scope: entry.scope }
401
+ );
402
+ }
403
+ return out;
404
+ }
405
+
406
+ /** Wait (bounded) for a secret row to appear — covers the replicated-deploy race where a peer runs
407
+ * the deploy before the origin's hdb_secret row has replicated in. */
408
+ async function waitForSecretRow(table: any, name: string, waitMs: number): Promise<any> {
409
+ const deadline = Date.now() + waitMs;
410
+ let row = await table.get(name);
411
+ while (!row && Date.now() < deadline) {
412
+ await new Promise((resolve) => setTimeout(resolve, Math.min(200, Math.max(1, waitMs))));
413
+ row = await table.get(name);
414
+ }
415
+ return row;
416
+ }
417
+
418
+ /**
419
+ * Resolve deploy_component `registryAuth` entries that reference a stored secret
420
+ * (`{ registry, secret }`) into concrete token entries (`{ registry, token }`) by decrypting the
421
+ * named hdb_secret row on this thread. Entries that already carry a literal `token` pass through
422
+ * unchanged, so this is a no-op for the token-only fallback form.
423
+ *
424
+ * This is intentionally NOT a `get_secret` operation — the store never returns plaintext across the
425
+ * API boundary. The resolved token is handed back to the install path in-memory, fed to the
426
+ * transient .npmrc, and never written back to the request body, replicated, or logged.
427
+ *
428
+ * Authority mirrors the accessor model (#1550): the referenced secret must be usable by the
429
+ * component being deployed — either a `processEnv` (global) secret or a scoped secret granted to
430
+ * `component`. Without that, the deploy path would let a super_user pull an arbitrary scoped secret
431
+ * into an .npmrc, sidestepping the grant that is the store's authority. A missing row, missing
432
+ * grant, absent custody, or decrypt failure throws a ClientError naming the precise reason.
433
+ *
434
+ * `options.waitMs` gives a bounded grace period for a referenced row to replicate in (used on the
435
+ * peer side of a replicated deploy, where the deploy op can arrive just ahead of the hdb_secret
436
+ * row); it defaults to 0 (the origin wrote the row before it resolves, so no wait is needed).
437
+ *
438
+ * Runs on the main thread, where the operations API dispatches deploys and the Pro secrets
439
+ * component registers custody — the same place set_secret decrypts. On a node without custody
440
+ * (OSS core, or a custody key not held here) a referenced secret cannot be resolved and the deploy
441
+ * fails loudly rather than silently installing without auth.
442
+ */
443
+ export async function resolveRegistryAuth(
444
+ registryAuth: any[] | undefined,
445
+ component: string,
446
+ options: { waitMs?: number } = {}
447
+ ): Promise<ResolvedRegistryAuthEntry[] | undefined> {
448
+ if (!Array.isArray(registryAuth) || registryAuth.length === 0) return registryAuth;
449
+ // Token-only requests must not require custody or a provisioned store — keep the fast path pure.
450
+ if (!registryAuth.some((entry) => entry && entry.secret !== undefined)) return registryAuth;
451
+
452
+ const waitMs = options.waitMs ?? 0;
453
+ const table = secretTable();
454
+ const resolved: ResolvedRegistryAuthEntry[] = [];
455
+ for (const entry of registryAuth) {
456
+ if (!entry) continue; // parity with the fast-path guard above; validated entries are never null
457
+ if (entry.secret === undefined) {
458
+ resolved.push({ registry: entry.registry, token: entry.token, scope: entry.scope });
459
+ continue;
460
+ }
461
+ const name: string = entry.secret;
462
+ const row = waitMs > 0 ? await waitForSecretRow(table, name, waitMs) : await table.get(name);
463
+ if (!row) {
464
+ throw new ClientError(
465
+ `registryAuth references secret '${name}', which does not exist`,
466
+ HTTP_STATUS_CODES.NOT_FOUND
467
+ );
468
+ }
469
+ const grants = Array.isArray(row.grants) ? row.grants : [];
470
+ if (!row.processEnv && !grants.includes(component)) {
471
+ throw new ClientError(
472
+ `registryAuth secret '${name}' is not granted to component '${component}' ` +
473
+ `(grant it with grant_secret, or set it processEnv:true)`,
474
+ HTTP_STATUS_CODES.FORBIDDEN
475
+ );
476
+ }
477
+ const custody = getSecretCustody();
478
+ if (!custody) {
479
+ // Server-state condition, not a client-fixable request: the same body would resolve once
480
+ // custody comes up, so report it retryable (503) rather than the ClientError default 400.
481
+ throw new ClientError(
482
+ `secrets custody is not initialized on this node; cannot resolve registryAuth secret '${name}'`,
483
+ HTTP_STATUS_CODES.SERVICE_UNAVAILABLE
484
+ );
485
+ }
486
+ let token: string;
487
+ try {
488
+ token = custody.decrypt(row.envelope);
489
+ } catch (error) {
490
+ throw new ClientError(
491
+ `Failed to decrypt registryAuth secret '${name}': ${(error as Error).message}`,
492
+ HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR
493
+ );
494
+ }
495
+ resolved.push({ registry: entry.registry, token, scope: entry.scope });
496
+ }
497
+ return resolved;
498
+ }
@@ -851,9 +851,33 @@ export function getConfiguration() {
851
851
  */
852
852
  export async function setConfiguration(setConfigJson) {
853
853
  // eslint-disable-next-line no-unused-vars
854
- const { operation, hdb_user, hdbAuthHeader, ...configFields } = setConfigJson;
854
+ const { operation, hdb_user, hdbAuthHeader, replicated, ...configFields } = setConfigJson;
855
+ // Operation-control field, not a config param: enforce boolean (matching other
856
+ // `replicated` surfaces, e.g. analyticsValidator) before any local write so a
857
+ // malformed value like the string "false" — which is truthy — can't apply config
858
+ // locally or trigger an unintended fan-out.
859
+ if (replicated !== undefined && typeof replicated !== 'boolean') {
860
+ throw handleHDBError(
861
+ new Error(),
862
+ `'replicated' must be a boolean`,
863
+ HTTP_STATUS_CODES.BAD_REQUEST,
864
+ undefined,
865
+ undefined,
866
+ true
867
+ );
868
+ }
855
869
  try {
856
870
  updateConfigValue(undefined, undefined, configFields, true);
871
+ if (replicated) {
872
+ // Opt-in fan-out to all cluster nodes (#660). replicateOperation forwards the
873
+ // body with `replicated: false`, so peers apply locally without re-replicating;
874
+ // per-node outcomes are returned on `response.replicated`. `replicated` must
875
+ // stay out of configFields on both origin and peers, or it would be written to
876
+ // the config file as a config param.
877
+ const response = await server.replication.replicateOperation(setConfigJson);
878
+ response.message = CONFIGURE_SUCCESS_RESPONSE;
879
+ return response;
880
+ }
857
881
  return CONFIGURE_SUCCESS_RESPONSE;
858
882
  } catch (err) {
859
883
  if (typeof err === 'string' || err instanceof String) {
@@ -51,6 +51,87 @@
51
51
  },
52
52
  "required": ["files"]
53
53
  },
54
+ "static": {
55
+ "type": "object",
56
+ "description": "Static file serving configuration.",
57
+ "additionalProperties": false,
58
+ "properties": {
59
+ "files": {
60
+ "description": "Glob(s) or path(s) to the files to serve.",
61
+ "oneOf": [
62
+ {
63
+ "type": "string"
64
+ },
65
+ {
66
+ "type": "array",
67
+ "items": {
68
+ "type": "string"
69
+ }
70
+ }
71
+ ]
72
+ },
73
+ "urlPath": {
74
+ "type": "string",
75
+ "description": "URL path prefix the files are served under."
76
+ },
77
+ "index": {
78
+ "type": "boolean",
79
+ "description": "Serve index.html files for directory requests.",
80
+ "default": true
81
+ },
82
+ "extensions": {
83
+ "type": "array",
84
+ "items": {
85
+ "type": "string"
86
+ },
87
+ "description": "File extensions to try appending when a requested path is not found (e.g. ['html'] serves /page.html for /page)."
88
+ },
89
+ "fallthrough": {
90
+ "type": "boolean",
91
+ "description": "Pass unmatched requests to the next handler. When false, this handler answers unmatched GETs itself — note it runs before the REST handler by default, so also set `after: 'rest'` if the application serves an API.",
92
+ "default": true
93
+ },
94
+ "notFound": {
95
+ "description": "File to serve when a path is not found (requires `fallthrough: false`). Either a path, or an object with `file` and `statusCode` (e.g. index.html with 200 for history-mode SPAs — combine with `after: 'rest'`).",
96
+ "oneOf": [
97
+ {
98
+ "type": "string"
99
+ },
100
+ {
101
+ "type": "object",
102
+ "additionalProperties": false,
103
+ "properties": {
104
+ "file": {
105
+ "type": "string"
106
+ },
107
+ "statusCode": {
108
+ "type": "number"
109
+ }
110
+ },
111
+ "required": ["file", "statusCode"]
112
+ }
113
+ ]
114
+ },
115
+ "before": {
116
+ "description": "Run this handler before the named handler (the config key as registered, e.g. 'rest'). Defaults to 'authentication' so file requests skip credential parsing; false clears the default so registration order applies.",
117
+ "oneOf": [
118
+ {
119
+ "type": "string",
120
+ "minLength": 1
121
+ },
122
+ {
123
+ "const": false
124
+ }
125
+ ]
126
+ },
127
+ "after": {
128
+ "type": "string",
129
+ "minLength": 1,
130
+ "description": "Run this handler after the named handler (the config key as registered, e.g. 'rest' to let API routes match before static fallbacks). Overrides the default `before: 'authentication'` hoist."
131
+ }
132
+ },
133
+ "required": ["files"]
134
+ },
54
135
  "package": {
55
136
  "type": "string",
56
137
  "description": "Package identifier for the application (npm-compatible or git URL)."
@@ -349,8 +349,15 @@
349
349
  "properties": {
350
350
  "enabled": { "type": "boolean", "description": "Enable log rotation. Default: true" },
351
351
  "compress": { "type": "boolean", "description": "Compress rotated logs with gzip. Default: false" },
352
- "interval": { "type": ["string", "null"], "description": "Time between rotations (e.g. '1D','6H','30M')." },
352
+ "interval": {
353
+ "type": ["string", "null"],
354
+ "description": "Time between rotations (e.g. '1D', '6H', '30m'). Units: D (days), H (hours), M (months), m (minutes)."
355
+ },
353
356
  "maxSize": { "type": ["string", "null"], "description": "Max size before rotation (e.g. '100M', '1G')." },
357
+ "retention": {
358
+ "type": ["string", "null"],
359
+ "description": "Delete rotated logs older than this age (e.g. '30D', '12H', '1M' for one month). Units: D (days), H (hours), M (months), m (minutes). When unset, rotated logs are kept indefinitely."
360
+ },
354
361
  "path": { "type": "string", "description": "Directory to store rotated logs. Default: <ROOTPATH>/log" }
355
362
  }
356
363
  },
@@ -86,9 +86,10 @@ function initSystemSchemaPaths(schema, table) {
86
86
  schema = schema.toString();
87
87
  table = table.toString();
88
88
 
89
- // Check to see if there are any CLI or env args related to schema/table path
90
- const args = process.env;
91
- Object.assign(args, minimist(process.argv));
89
+ // Check to see if there are any CLI or env args related to schema/table path.
90
+ // Merge CLI args over env vars WITHOUT mutating process.env: assigning into process.env
91
+ // permanently clobbered real env vars (e.g. AUTHENTICATION_AUTHORIZELOCAL) with CLI values.
92
+ const args = Object.assign({}, process.env, minimist(process.argv));
92
93
 
93
94
  const schemaConfJson = args[CONFIG_PARAMS.DATABASES.toUpperCase()];
94
95
  if (schemaConfJson) {
@@ -27,6 +27,20 @@ export default async function readAuditLog(readAuditLogObject: any) {
27
27
  throw new Error(HDB_ERROR_MSGS.TABLE_REQUIRED_ERR);
28
28
  }
29
29
 
30
+ // system.hdb_secret audit rows carry full record images — i.e. secret envelopes — and
31
+ // read_audit_log is delegable to non-super_user roles via the `operations` allowlist. Table
32
+ // audit itself must stay on (mount_hdb forces audit:true for every system table; on RocksDB it
33
+ // is load-bearing for data retention), so the read surface is blocked instead, regardless of
34
+ // config or role. Secret mutations are separately audited via value-free logger.notify events
35
+ // (components/secretOperations.ts).
36
+ if (
37
+ database === hdbTerms.SYSTEM_SCHEMA_NAME &&
38
+ readAuditLogObject.table === hdbTerms.SYSTEM_TABLE_NAMES.SECRET_TABLE_NAME
39
+ ) {
40
+ const msg = `read_audit_log is not supported on ${database}.${readAuditLogObject.table}; secret mutations are audited via structured log events that never contain values`;
41
+ throw handleHDBError(new Error(), msg, HTTP_STATUS_CODES.FORBIDDEN, undefined, undefined, true);
42
+ }
43
+
30
44
  if (!envMgr.get(hdbTerms.CONFIG_PARAMS.LOGGING_AUDITLOG)) {
31
45
  throw handleHDBError(
32
46
  new Error(),
@@ -204,6 +204,7 @@ async function descTable(describeTableObject: any, attrPerms?: any) {
204
204
  if (tableObj.replicate !== undefined) tableResult.replicate = tableObj.replicate;
205
205
  if (tableObj.expirationMS !== undefined) tableResult.expiration = tableObj.expirationMS / 1000 + 's';
206
206
  if (tableObj.sealed !== undefined) tableResult.sealed = tableObj.sealed;
207
+ if (tableObj.cacheControl != null) tableResult.cacheControl = tableObj.cacheControl;
207
208
  if ((tableObj as any).sources?.length > 0)
208
209
  tableResult.sources = (tableObj as any).sources
209
210
  .map((source: any) => source.name)