@enbox/dwn-server 0.0.2 → 0.0.4

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 (309) hide show
  1. package/LICENSE +3 -2
  2. package/README.md +115 -215
  3. package/dist/esm/src/admin/activity-log.d.ts +44 -0
  4. package/dist/esm/src/admin/activity-log.d.ts.map +1 -0
  5. package/dist/esm/src/admin/activity-log.js +85 -0
  6. package/dist/esm/src/admin/activity-log.js.map +1 -0
  7. package/dist/esm/src/admin/admin-api.d.ts +61 -0
  8. package/dist/esm/src/admin/admin-api.d.ts.map +1 -0
  9. package/dist/esm/src/admin/admin-api.js +1047 -0
  10. package/dist/esm/src/admin/admin-api.js.map +1 -0
  11. package/dist/esm/src/admin/admin-auth.d.ts +9 -0
  12. package/dist/esm/src/admin/admin-auth.d.ts.map +1 -0
  13. package/dist/esm/src/admin/admin-auth.js +45 -0
  14. package/dist/esm/src/admin/admin-auth.js.map +1 -0
  15. package/dist/esm/src/admin/admin-store.d.ts +111 -0
  16. package/dist/esm/src/admin/admin-store.d.ts.map +1 -0
  17. package/dist/esm/src/admin/admin-store.js +376 -0
  18. package/dist/esm/src/admin/admin-store.js.map +1 -0
  19. package/dist/esm/src/admin/audit-log.d.ts +94 -0
  20. package/dist/esm/src/admin/audit-log.d.ts.map +1 -0
  21. package/dist/esm/src/admin/audit-log.js +220 -0
  22. package/dist/esm/src/admin/audit-log.js.map +1 -0
  23. package/dist/esm/src/admin/index.d.ts +10 -0
  24. package/dist/esm/src/admin/index.d.ts.map +1 -0
  25. package/dist/esm/src/admin/index.js +7 -0
  26. package/dist/esm/src/admin/index.js.map +1 -0
  27. package/dist/esm/src/admin/types.d.ts +306 -0
  28. package/dist/esm/src/admin/types.d.ts.map +1 -0
  29. package/dist/esm/src/admin/types.js +2 -0
  30. package/dist/esm/src/admin/types.js.map +1 -0
  31. package/dist/esm/src/admin/webhook-manager.d.ts +55 -0
  32. package/dist/esm/src/admin/webhook-manager.d.ts.map +1 -0
  33. package/dist/esm/src/admin/webhook-manager.js +184 -0
  34. package/dist/esm/src/admin/webhook-manager.js.map +1 -0
  35. package/dist/esm/src/config.d.ts +124 -9
  36. package/dist/esm/src/config.d.ts.map +1 -1
  37. package/dist/esm/src/config.js +155 -13
  38. package/dist/esm/src/config.js.map +1 -1
  39. package/dist/esm/src/connection/connection-manager.d.ts +32 -9
  40. package/dist/esm/src/connection/connection-manager.d.ts.map +1 -1
  41. package/dist/esm/src/connection/connection-manager.js +38 -5
  42. package/dist/esm/src/connection/connection-manager.js.map +1 -1
  43. package/dist/esm/src/connection/flow-controller.d.ts +53 -0
  44. package/dist/esm/src/connection/flow-controller.d.ts.map +1 -0
  45. package/dist/esm/src/connection/flow-controller.js +101 -0
  46. package/dist/esm/src/connection/flow-controller.js.map +1 -0
  47. package/dist/esm/src/connection/socket-connection.d.ts +54 -18
  48. package/dist/esm/src/connection/socket-connection.d.ts.map +1 -1
  49. package/dist/esm/src/connection/socket-connection.js +102 -40
  50. package/dist/esm/src/connection/socket-connection.js.map +1 -1
  51. package/dist/esm/src/delivery-service.d.ts +43 -0
  52. package/dist/esm/src/delivery-service.d.ts.map +1 -0
  53. package/dist/esm/src/delivery-service.js +574 -0
  54. package/dist/esm/src/delivery-service.js.map +1 -0
  55. package/dist/esm/src/dwn-error.d.ts +10 -1
  56. package/dist/esm/src/dwn-error.d.ts.map +1 -1
  57. package/dist/esm/src/dwn-error.js +9 -0
  58. package/dist/esm/src/dwn-error.js.map +1 -1
  59. package/dist/esm/src/dwn-server.d.ts +13 -6
  60. package/dist/esm/src/dwn-server.d.ts.map +1 -1
  61. package/dist/esm/src/dwn-server.js +199 -24
  62. package/dist/esm/src/dwn-server.js.map +1 -1
  63. package/dist/esm/src/http-api.d.ts +28 -13
  64. package/dist/esm/src/http-api.d.ts.map +1 -1
  65. package/dist/esm/src/http-api.js +649 -374
  66. package/dist/esm/src/http-api.js.map +1 -1
  67. package/dist/esm/src/index.d.ts +6 -2
  68. package/dist/esm/src/index.d.ts.map +1 -1
  69. package/dist/esm/src/index.js +4 -1
  70. package/dist/esm/src/index.js.map +1 -1
  71. package/dist/esm/src/json-rpc-api.js +2 -1
  72. package/dist/esm/src/json-rpc-api.js.map +1 -1
  73. package/dist/esm/src/json-rpc-handlers/dwn/process-message.d.ts.map +1 -1
  74. package/dist/esm/src/json-rpc-handlers/dwn/process-message.js +109 -7
  75. package/dist/esm/src/json-rpc-handlers/dwn/process-message.js.map +1 -1
  76. package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts +20 -0
  77. package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts.map +1 -0
  78. package/dist/esm/src/json-rpc-handlers/subscription/ack.js +41 -0
  79. package/dist/esm/src/json-rpc-handlers/subscription/ack.js.map +1 -0
  80. package/dist/esm/src/json-rpc-handlers/subscription/close.d.ts.map +1 -1
  81. package/dist/esm/src/json-rpc-handlers/subscription/close.js +1 -1
  82. package/dist/esm/src/json-rpc-handlers/subscription/close.js.map +1 -1
  83. package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts +1 -0
  84. package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts.map +1 -1
  85. package/dist/esm/src/json-rpc-handlers/subscription/index.js +1 -0
  86. package/dist/esm/src/json-rpc-handlers/subscription/index.js.map +1 -1
  87. package/dist/esm/src/lib/json-rpc-router.d.ts +25 -8
  88. package/dist/esm/src/lib/json-rpc-router.d.ts.map +1 -1
  89. package/dist/esm/src/lib/json-rpc-router.js.map +1 -1
  90. package/dist/esm/src/lib/sql-utils.d.ts +6 -0
  91. package/dist/esm/src/lib/sql-utils.d.ts.map +1 -0
  92. package/dist/esm/src/lib/sql-utils.js +8 -0
  93. package/dist/esm/src/lib/sql-utils.js.map +1 -0
  94. package/dist/esm/src/main.js +0 -6
  95. package/dist/esm/src/main.js.map +1 -1
  96. package/dist/esm/src/message-processed-hook.d.ts +35 -0
  97. package/dist/esm/src/message-processed-hook.d.ts.map +1 -0
  98. package/dist/esm/src/message-processed-hook.js +2 -0
  99. package/dist/esm/src/message-processed-hook.js.map +1 -0
  100. package/dist/esm/src/metrics.d.ts +14 -2
  101. package/dist/esm/src/metrics.d.ts.map +1 -1
  102. package/dist/esm/src/metrics.js +41 -1
  103. package/dist/esm/src/metrics.js.map +1 -1
  104. package/dist/esm/src/plugins/event-log-nats.d.ts +25 -0
  105. package/dist/esm/src/plugins/event-log-nats.d.ts.map +1 -0
  106. package/dist/esm/src/plugins/event-log-nats.js +379 -0
  107. package/dist/esm/src/plugins/event-log-nats.js.map +1 -0
  108. package/dist/esm/src/rate-limiter.d.ts +60 -0
  109. package/dist/esm/src/rate-limiter.d.ts.map +1 -0
  110. package/dist/esm/src/rate-limiter.js +116 -0
  111. package/dist/esm/src/rate-limiter.js.map +1 -0
  112. package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts +53 -0
  113. package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts.map +1 -0
  114. package/dist/esm/src/registration/jwt-provider-auth-plugin.js +90 -0
  115. package/dist/esm/src/registration/jwt-provider-auth-plugin.js.map +1 -0
  116. package/dist/esm/src/registration/open-auth-handler.d.ts +37 -0
  117. package/dist/esm/src/registration/open-auth-handler.d.ts.map +1 -0
  118. package/dist/esm/src/registration/open-auth-handler.js +214 -0
  119. package/dist/esm/src/registration/open-auth-handler.js.map +1 -0
  120. package/dist/esm/src/registration/proof-of-work-manager.d.ts +1 -1
  121. package/dist/esm/src/registration/proof-of-work-manager.d.ts.map +1 -1
  122. package/dist/esm/src/registration/proof-of-work-manager.js +3 -3
  123. package/dist/esm/src/registration/proof-of-work-manager.js.map +1 -1
  124. package/dist/esm/src/registration/provider-auth-plugin.d.ts +46 -0
  125. package/dist/esm/src/registration/provider-auth-plugin.d.ts.map +1 -0
  126. package/dist/esm/src/registration/provider-auth-plugin.js +29 -0
  127. package/dist/esm/src/registration/provider-auth-plugin.js.map +1 -0
  128. package/dist/esm/src/registration/registration-manager.d.ts +28 -5
  129. package/dist/esm/src/registration/registration-manager.d.ts.map +1 -1
  130. package/dist/esm/src/registration/registration-manager.js +83 -12
  131. package/dist/esm/src/registration/registration-manager.js.map +1 -1
  132. package/dist/esm/src/registration/registration-store.d.ts +83 -3
  133. package/dist/esm/src/registration/registration-store.d.ts.map +1 -1
  134. package/dist/esm/src/registration/registration-store.js +248 -11
  135. package/dist/esm/src/registration/registration-store.js.map +1 -1
  136. package/dist/esm/src/storage.d.ts +5 -5
  137. package/dist/esm/src/storage.d.ts.map +1 -1
  138. package/dist/esm/src/storage.js +105 -24
  139. package/dist/esm/src/storage.js.map +1 -1
  140. package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts.map +1 -1
  141. package/dist/esm/src/web5-connect/sql-ttl-cache.js +11 -3
  142. package/dist/esm/src/web5-connect/sql-ttl-cache.js.map +1 -1
  143. package/dist/esm/src/web5-connect/web5-connect-server.d.ts.map +1 -1
  144. package/dist/esm/src/web5-connect/web5-connect-server.js +2 -2
  145. package/dist/esm/src/web5-connect/web5-connect-server.js.map +1 -1
  146. package/dist/esm/src/ws-api.d.ts +18 -4
  147. package/dist/esm/src/ws-api.d.ts.map +1 -1
  148. package/dist/esm/src/ws-api.js +12 -16
  149. package/dist/esm/src/ws-api.js.map +1 -1
  150. package/package.json +34 -53
  151. package/src/admin/activity-log.ts +100 -0
  152. package/src/admin/admin-api.ts +1308 -0
  153. package/src/admin/admin-auth.ts +56 -0
  154. package/src/admin/admin-store.ts +515 -0
  155. package/src/admin/audit-log.ts +327 -0
  156. package/src/admin/index.ts +34 -0
  157. package/src/admin/types.ts +352 -0
  158. package/src/admin/webhook-manager.ts +245 -0
  159. package/src/config.ts +190 -22
  160. package/src/connection/connection-manager.ts +67 -17
  161. package/src/connection/flow-controller.ts +117 -0
  162. package/src/connection/socket-connection.ts +144 -67
  163. package/src/delivery-service.ts +740 -0
  164. package/src/dwn-error.ts +11 -2
  165. package/src/dwn-server.ts +254 -39
  166. package/src/http-api.ts +736 -392
  167. package/src/index.ts +13 -2
  168. package/src/json-rpc-api.ts +2 -1
  169. package/src/json-rpc-handlers/dwn/process-message.ts +149 -15
  170. package/src/json-rpc-handlers/subscription/ack.ts +63 -0
  171. package/src/json-rpc-handlers/subscription/close.ts +5 -9
  172. package/src/json-rpc-handlers/subscription/index.ts +1 -0
  173. package/src/lib/json-rpc-router.ts +26 -11
  174. package/src/lib/sql-utils.ts +7 -0
  175. package/src/main.ts +0 -8
  176. package/src/message-processed-hook.ts +33 -0
  177. package/src/metrics.ts +57 -8
  178. package/src/plugins/event-log-nats.ts +466 -0
  179. package/src/process-handlers.ts +5 -5
  180. package/src/rate-limiter.ts +143 -0
  181. package/src/registration/jwt-provider-auth-plugin.ts +119 -0
  182. package/src/registration/open-auth-handler.ts +263 -0
  183. package/src/registration/proof-of-work-manager.ts +11 -10
  184. package/src/registration/provider-auth-plugin.ts +84 -0
  185. package/src/registration/registration-manager.ts +129 -31
  186. package/src/registration/registration-store.ts +332 -22
  187. package/src/storage.ts +136 -40
  188. package/src/web5-connect/sql-ttl-cache.ts +12 -5
  189. package/src/web5-connect/web5-connect-server.ts +9 -8
  190. package/src/ws-api.ts +39 -26
  191. package/dist/cjs/index.js +0 -6811
  192. package/dist/cjs/package.json +0 -1
  193. package/dist/esm/src/json-rpc-socket.d.ts +0 -39
  194. package/dist/esm/src/json-rpc-socket.d.ts.map +0 -1
  195. package/dist/esm/src/json-rpc-socket.js +0 -125
  196. package/dist/esm/src/json-rpc-socket.js.map +0 -1
  197. package/dist/esm/src/lib/http-server-shutdown-handler.d.ts +0 -10
  198. package/dist/esm/src/lib/http-server-shutdown-handler.d.ts.map +0 -1
  199. package/dist/esm/src/lib/http-server-shutdown-handler.js +0 -65
  200. package/dist/esm/src/lib/http-server-shutdown-handler.js.map +0 -1
  201. package/dist/esm/src/lib/json-rpc.d.ts +0 -54
  202. package/dist/esm/src/lib/json-rpc.d.ts.map +0 -1
  203. package/dist/esm/src/lib/json-rpc.js +0 -60
  204. package/dist/esm/src/lib/json-rpc.js.map +0 -1
  205. package/dist/esm/src/registration/proof-of-work-types.d.ts +0 -8
  206. package/dist/esm/src/registration/proof-of-work-types.d.ts.map +0 -1
  207. package/dist/esm/src/registration/proof-of-work-types.js +0 -2
  208. package/dist/esm/src/registration/proof-of-work-types.js.map +0 -1
  209. package/dist/esm/src/registration/registration-types.d.ts +0 -18
  210. package/dist/esm/src/registration/registration-types.d.ts.map +0 -1
  211. package/dist/esm/src/registration/registration-types.js +0 -2
  212. package/dist/esm/src/registration/registration-types.js.map +0 -1
  213. package/dist/esm/tests/common-scenario-validator.d.ts +0 -11
  214. package/dist/esm/tests/common-scenario-validator.d.ts.map +0 -1
  215. package/dist/esm/tests/common-scenario-validator.js +0 -114
  216. package/dist/esm/tests/common-scenario-validator.js.map +0 -1
  217. package/dist/esm/tests/connection/connection-manager.spec.d.ts +0 -2
  218. package/dist/esm/tests/connection/connection-manager.spec.d.ts.map +0 -1
  219. package/dist/esm/tests/connection/connection-manager.spec.js +0 -47
  220. package/dist/esm/tests/connection/connection-manager.spec.js.map +0 -1
  221. package/dist/esm/tests/connection/socket-connection.spec.d.ts +0 -2
  222. package/dist/esm/tests/connection/socket-connection.spec.d.ts.map +0 -1
  223. package/dist/esm/tests/connection/socket-connection.spec.js +0 -125
  224. package/dist/esm/tests/connection/socket-connection.spec.js.map +0 -1
  225. package/dist/esm/tests/cors/http-api.browser.d.ts +0 -2
  226. package/dist/esm/tests/cors/http-api.browser.d.ts.map +0 -1
  227. package/dist/esm/tests/cors/http-api.browser.js +0 -60
  228. package/dist/esm/tests/cors/http-api.browser.js.map +0 -1
  229. package/dist/esm/tests/cors/ping.browser.d.ts +0 -2
  230. package/dist/esm/tests/cors/ping.browser.d.ts.map +0 -1
  231. package/dist/esm/tests/cors/ping.browser.js +0 -7
  232. package/dist/esm/tests/cors/ping.browser.js.map +0 -1
  233. package/dist/esm/tests/dwn-process-message.spec.d.ts +0 -2
  234. package/dist/esm/tests/dwn-process-message.spec.d.ts.map +0 -1
  235. package/dist/esm/tests/dwn-process-message.spec.js +0 -172
  236. package/dist/esm/tests/dwn-process-message.spec.js.map +0 -1
  237. package/dist/esm/tests/dwn-server.spec.d.ts +0 -2
  238. package/dist/esm/tests/dwn-server.spec.d.ts.map +0 -1
  239. package/dist/esm/tests/dwn-server.spec.js +0 -49
  240. package/dist/esm/tests/dwn-server.spec.js.map +0 -1
  241. package/dist/esm/tests/http-api.spec.d.ts +0 -2
  242. package/dist/esm/tests/http-api.spec.d.ts.map +0 -1
  243. package/dist/esm/tests/http-api.spec.js +0 -775
  244. package/dist/esm/tests/http-api.spec.js.map +0 -1
  245. package/dist/esm/tests/json-rpc-socket.spec.d.ts +0 -2
  246. package/dist/esm/tests/json-rpc-socket.spec.d.ts.map +0 -1
  247. package/dist/esm/tests/json-rpc-socket.spec.js +0 -225
  248. package/dist/esm/tests/json-rpc-socket.spec.js.map +0 -1
  249. package/dist/esm/tests/plugins/data-store-sqlite.d.ts +0 -17
  250. package/dist/esm/tests/plugins/data-store-sqlite.d.ts.map +0 -1
  251. package/dist/esm/tests/plugins/data-store-sqlite.js +0 -23
  252. package/dist/esm/tests/plugins/data-store-sqlite.js.map +0 -1
  253. package/dist/esm/tests/plugins/event-log-sqlite.d.ts +0 -17
  254. package/dist/esm/tests/plugins/event-log-sqlite.d.ts.map +0 -1
  255. package/dist/esm/tests/plugins/event-log-sqlite.js +0 -23
  256. package/dist/esm/tests/plugins/event-log-sqlite.js.map +0 -1
  257. package/dist/esm/tests/plugins/event-stream-in-memory.d.ts +0 -17
  258. package/dist/esm/tests/plugins/event-stream-in-memory.d.ts.map +0 -1
  259. package/dist/esm/tests/plugins/event-stream-in-memory.js +0 -21
  260. package/dist/esm/tests/plugins/event-stream-in-memory.js.map +0 -1
  261. package/dist/esm/tests/plugins/message-store-sqlite.d.ts +0 -17
  262. package/dist/esm/tests/plugins/message-store-sqlite.d.ts.map +0 -1
  263. package/dist/esm/tests/plugins/message-store-sqlite.js +0 -23
  264. package/dist/esm/tests/plugins/message-store-sqlite.js.map +0 -1
  265. package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts +0 -17
  266. package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts.map +0 -1
  267. package/dist/esm/tests/plugins/resumable-task-store-sqlite.js +0 -23
  268. package/dist/esm/tests/plugins/resumable-task-store-sqlite.js.map +0 -1
  269. package/dist/esm/tests/process-handler.spec.d.ts +0 -2
  270. package/dist/esm/tests/process-handler.spec.d.ts.map +0 -1
  271. package/dist/esm/tests/process-handler.spec.js +0 -60
  272. package/dist/esm/tests/process-handler.spec.js.map +0 -1
  273. package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts +0 -2
  274. package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts.map +0 -1
  275. package/dist/esm/tests/registration/proof-of-work-manager.spec.js +0 -157
  276. package/dist/esm/tests/registration/proof-of-work-manager.spec.js.map +0 -1
  277. package/dist/esm/tests/rpc-subscribe-close.spec.d.ts +0 -2
  278. package/dist/esm/tests/rpc-subscribe-close.spec.d.ts.map +0 -1
  279. package/dist/esm/tests/rpc-subscribe-close.spec.js +0 -81
  280. package/dist/esm/tests/rpc-subscribe-close.spec.js.map +0 -1
  281. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts +0 -2
  282. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts.map +0 -1
  283. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js +0 -73
  284. package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js.map +0 -1
  285. package/dist/esm/tests/scenarios/registration.spec.d.ts +0 -2
  286. package/dist/esm/tests/scenarios/registration.spec.d.ts.map +0 -1
  287. package/dist/esm/tests/scenarios/registration.spec.js +0 -507
  288. package/dist/esm/tests/scenarios/registration.spec.js.map +0 -1
  289. package/dist/esm/tests/scenarios/web5-connect.spec.d.ts +0 -2
  290. package/dist/esm/tests/scenarios/web5-connect.spec.d.ts.map +0 -1
  291. package/dist/esm/tests/scenarios/web5-connect.spec.js +0 -137
  292. package/dist/esm/tests/scenarios/web5-connect.spec.js.map +0 -1
  293. package/dist/esm/tests/test-dwn.d.ts +0 -7
  294. package/dist/esm/tests/test-dwn.d.ts.map +0 -1
  295. package/dist/esm/tests/test-dwn.js +0 -34
  296. package/dist/esm/tests/test-dwn.js.map +0 -1
  297. package/dist/esm/tests/utils.d.ts +0 -46
  298. package/dist/esm/tests/utils.d.ts.map +0 -1
  299. package/dist/esm/tests/utils.js +0 -116
  300. package/dist/esm/tests/utils.js.map +0 -1
  301. package/dist/esm/tests/ws-api.spec.d.ts +0 -2
  302. package/dist/esm/tests/ws-api.spec.d.ts.map +0 -1
  303. package/dist/esm/tests/ws-api.spec.js +0 -327
  304. package/dist/esm/tests/ws-api.spec.js.map +0 -1
  305. package/src/json-rpc-socket.ts +0 -155
  306. package/src/lib/http-server-shutdown-handler.ts +0 -79
  307. package/src/lib/json-rpc.ts +0 -126
  308. package/src/registration/proof-of-work-types.ts +0 -7
  309. package/src/registration/registration-types.ts +0 -18
@@ -0,0 +1,327 @@
1
+ import type { Dialect } from '@enbox/dwn-sql-store';
2
+
3
+ import { Kysely } from 'kysely';
4
+ import log from 'loglevel';
5
+
6
+ import { escapeLikeWildcards } from '../lib/sql-utils.js';
7
+
8
+ /**
9
+ * Input for recording a new audit event.
10
+ */
11
+ export type AuditEventInput = {
12
+ /** The actor that performed the action (`system` or an admin identifier). */
13
+ actor : string;
14
+ /** Dot-delimited action identifier (e.g. `tenant.suspend`, `quota.update`). */
15
+ action : string;
16
+ /** Target of the action (typically a tenant DID or resource identifier). */
17
+ target? : string;
18
+ /** Additional detail — stored as a JSON string. */
19
+ detail? : string;
20
+ };
21
+
22
+ /**
23
+ * A persisted audit event row.
24
+ */
25
+ export type AuditEvent = AuditEventInput & {
26
+ /** Auto-incremented row ID. */
27
+ id : number;
28
+ /** ISO-8601 timestamp when the event was recorded. */
29
+ timestamp : string;
30
+ };
31
+
32
+ /**
33
+ * Query options for retrieving audit events.
34
+ */
35
+ export type AuditQueryOptions = {
36
+ /** Return events with timestamp >= this ISO-8601 value. */
37
+ since? : string;
38
+ /** Filter by action (exact match or prefix with `*`). */
39
+ action? : string;
40
+ /** Filter by target DID or resource. */
41
+ target? : string;
42
+ /** Maximum number of results. Default: 50. */
43
+ limit? : number;
44
+ /** Cursor (row ID) for keyset pagination. */
45
+ cursor? : number;
46
+ };
47
+
48
+ /**
49
+ * Persistent, append-only audit log backed by a SQL table.
50
+ *
51
+ * Records significant admin and system events. Queryable via
52
+ * `GET /admin/api/audit`.
53
+ *
54
+ * @see https://github.com/enboxorg/enbox/issues/327
55
+ */
56
+ /**
57
+ * Configuration for audit log retention.
58
+ *
59
+ * @see https://github.com/enboxorg/enbox/issues/394
60
+ */
61
+ export type AuditRetentionConfig = {
62
+ /** Maximum age in days. 0 = no limit. */
63
+ maxAgeDays : number;
64
+ /** Maximum row count. 0 = no limit. */
65
+ maxRows : number;
66
+ };
67
+
68
+ export class AuditLog {
69
+ static readonly #tableName = 'adminAuditLog';
70
+ /** Cleanup runs every hour. */
71
+ static readonly #CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
72
+
73
+ #db: Kysely<AuditDatabase>;
74
+ #retentionConfig: AuditRetentionConfig | undefined;
75
+ #cleanupInterval: ReturnType<typeof setInterval> | undefined;
76
+
77
+ private constructor(dialect: Dialect) {
78
+ this.#db = new Kysely<AuditDatabase>({ dialect });
79
+ }
80
+
81
+ /**
82
+ * Creates and initializes an `AuditLog` instance.
83
+ * Creates the `adminAuditLog` table if it does not already exist.
84
+ * If `retentionConfig` is provided, starts periodic cleanup.
85
+ */
86
+ public static async create(dialect: Dialect, retentionConfig?: AuditRetentionConfig): Promise<AuditLog> {
87
+ const auditLog = new AuditLog(dialect);
88
+ await auditLog.#initialize();
89
+
90
+ if (retentionConfig && (retentionConfig.maxAgeDays > 0 || retentionConfig.maxRows > 0)) {
91
+ auditLog.#retentionConfig = retentionConfig;
92
+ auditLog.#startRetentionCleanup();
93
+ }
94
+
95
+ return auditLog;
96
+ }
97
+
98
+ /**
99
+ * Creates the audit log table and indices if they do not exist.
100
+ */
101
+ async #initialize(): Promise<void> {
102
+ await this.#db.schema
103
+ .createTable(AuditLog.#tableName)
104
+ .ifNotExists()
105
+ .addColumn('id', 'integer', (col) => col.primaryKey().autoIncrement())
106
+ .addColumn('timestamp', 'text', (col) => col.notNull())
107
+ .addColumn('actor', 'text', (col) => col.notNull())
108
+ .addColumn('action', 'text', (col) => col.notNull())
109
+ .addColumn('target', 'text')
110
+ .addColumn('detail', 'text')
111
+ .execute();
112
+
113
+ // Indices for common query patterns. Wrapped in try/catch because
114
+ // `CREATE INDEX IF NOT EXISTS` syntax varies across dialects.
115
+ try {
116
+ await this.#db.schema
117
+ .createIndex('index_audit_timestamp')
118
+ .ifNotExists()
119
+ .on(AuditLog.#tableName)
120
+ .column('timestamp')
121
+ .execute();
122
+ } catch {
123
+ // Index already exists.
124
+ }
125
+
126
+ try {
127
+ await this.#db.schema
128
+ .createIndex('index_audit_target')
129
+ .ifNotExists()
130
+ .on(AuditLog.#tableName)
131
+ .column('target')
132
+ .execute();
133
+ } catch {
134
+ // Index already exists.
135
+ }
136
+
137
+ try {
138
+ await this.#db.schema
139
+ .createIndex('index_audit_action')
140
+ .ifNotExists()
141
+ .on(AuditLog.#tableName)
142
+ .column('action')
143
+ .execute();
144
+ } catch {
145
+ // Index already exists.
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Records a new audit event.
151
+ */
152
+ public async record(event: AuditEventInput): Promise<void> {
153
+ await this.#db
154
+ .insertInto(AuditLog.#tableName)
155
+ .values({
156
+ timestamp : new Date().toISOString(),
157
+ actor : event.actor,
158
+ action : event.action,
159
+ target : event.target ?? null,
160
+ detail : event.detail ?? null,
161
+ })
162
+ .execute();
163
+ }
164
+
165
+ /**
166
+ * Queries audit events with optional filtering and pagination.
167
+ */
168
+ public async query(options?: AuditQueryOptions): Promise<{ events: AuditEvent[]; cursor?: number }> {
169
+ const limit = Math.min(options?.limit ?? 50, 1000);
170
+
171
+ let query = this.#db
172
+ .selectFrom(AuditLog.#tableName)
173
+ .selectAll()
174
+ .orderBy('id', 'desc')
175
+ .limit(limit + 1); // fetch one extra to detect next page
176
+
177
+ if (options?.cursor !== undefined) {
178
+ query = query.where('id', '<', options.cursor);
179
+ }
180
+
181
+ if (options?.since !== undefined) {
182
+ query = query.where('timestamp', '>=', options.since);
183
+ }
184
+
185
+ if (options?.action !== undefined) {
186
+ if (options.action.endsWith('*')) {
187
+ const prefix = escapeLikeWildcards(options.action.slice(0, -1));
188
+ query = query.where('action', 'like', `${prefix}%`);
189
+ } else {
190
+ query = query.where('action', '=', options.action);
191
+ }
192
+ }
193
+
194
+ if (options?.target !== undefined) {
195
+ query = query.where('target', '=', options.target);
196
+ }
197
+
198
+ const results = await query.execute();
199
+
200
+ const events: AuditEvent[] = results.map((row): AuditEvent => ({
201
+ id : Number(row.id),
202
+ timestamp : row.timestamp,
203
+ actor : row.actor,
204
+ action : row.action,
205
+ target : row.target ?? undefined,
206
+ detail : row.detail ?? undefined,
207
+ }));
208
+
209
+ let cursor: number | undefined;
210
+ if (events.length > limit) {
211
+ events.pop();
212
+ cursor = events[events.length - 1].id;
213
+ }
214
+
215
+ return { events, cursor };
216
+ }
217
+
218
+ /**
219
+ * Returns the total number of audit events.
220
+ */
221
+ public async count(): Promise<number> {
222
+ const result = await this.#db
223
+ .selectFrom(AuditLog.#tableName)
224
+ .select(this.#db.fn.countAll<number>().as('count'))
225
+ .executeTakeFirstOrThrow();
226
+
227
+ return Number(result.count);
228
+ }
229
+
230
+ /**
231
+ * Runs retention cleanup: purges entries older than maxAgeDays and trims
232
+ * the table to maxRows. Returns the total number of rows deleted.
233
+ *
234
+ * @see https://github.com/enboxorg/enbox/issues/394
235
+ */
236
+ public async enforceRetention(config?: AuditRetentionConfig): Promise<number> {
237
+ const rc = config ?? this.#retentionConfig;
238
+ if (!rc) {
239
+ return 0;
240
+ }
241
+
242
+ let totalDeleted = 0;
243
+
244
+ // Purge by age.
245
+ if (rc.maxAgeDays > 0) {
246
+ const cutoff = new Date(Date.now() - rc.maxAgeDays * 24 * 60 * 60 * 1000).toISOString();
247
+ const result = await this.#db
248
+ .deleteFrom(AuditLog.#tableName)
249
+ .where('timestamp', '<', cutoff)
250
+ .executeTakeFirstOrThrow();
251
+ totalDeleted += Number(result.numDeletedRows);
252
+ }
253
+
254
+ // Purge excess rows (keep most recent).
255
+ if (rc.maxRows > 0) {
256
+ const currentCount = await this.count();
257
+ if (currentCount > rc.maxRows) {
258
+ const excess = currentCount - rc.maxRows;
259
+ // Delete the oldest `excess` rows using a subquery.
260
+ const oldestRows = await this.#db
261
+ .selectFrom(AuditLog.#tableName)
262
+ .select('id')
263
+ .orderBy('id', 'asc')
264
+ .limit(excess)
265
+ .execute();
266
+
267
+ if (oldestRows.length > 0) {
268
+ const maxIdToDelete = oldestRows[oldestRows.length - 1].id;
269
+ const result = await this.#db
270
+ .deleteFrom(AuditLog.#tableName)
271
+ .where('id', '<=', maxIdToDelete)
272
+ .executeTakeFirstOrThrow();
273
+ totalDeleted += Number(result.numDeletedRows);
274
+ }
275
+ }
276
+ }
277
+
278
+ return totalDeleted;
279
+ }
280
+
281
+ /**
282
+ * Starts the periodic retention cleanup timer.
283
+ */
284
+ #startRetentionCleanup(): void {
285
+ if (this.#cleanupInterval) {
286
+ return;
287
+ }
288
+
289
+ this.#cleanupInterval = setInterval((): void => {
290
+ this.enforceRetention().then((deleted): void => {
291
+ if (deleted > 0) {
292
+ log.info(`Audit log retention: purged ${deleted} old entries`);
293
+ }
294
+ }).catch((err): void => {
295
+ log.error('Audit log retention cleanup failed:', err);
296
+ });
297
+ }, AuditLog.#CLEANUP_INTERVAL_MS);
298
+ }
299
+
300
+ /**
301
+ * Closes the underlying database connection and stops the retention timer.
302
+ */
303
+ public async close(): Promise<void> {
304
+ if (this.#cleanupInterval) {
305
+ clearInterval(this.#cleanupInterval);
306
+ this.#cleanupInterval = undefined;
307
+ }
308
+ await this.#db.destroy();
309
+ }
310
+ }
311
+
312
+ // ---------------------------------------------------------------------------
313
+ // Kysely type definitions
314
+ // ---------------------------------------------------------------------------
315
+
316
+ interface AuditLogRow {
317
+ id : number;
318
+ timestamp : string;
319
+ actor : string;
320
+ action : string;
321
+ target : string | null;
322
+ detail : string | null;
323
+ }
324
+
325
+ interface AuditDatabase {
326
+ adminAuditLog : AuditLogRow;
327
+ }
@@ -0,0 +1,34 @@
1
+ export { ActivityLog } from './activity-log.js';
2
+ export { AdminApi } from './admin-api.js';
3
+ export { AdminStore } from './admin-store.js';
4
+ export { AuditLog } from './audit-log.js';
5
+ export { validateAdminAuth } from './admin-auth.js';
6
+ export { WebhookManager } from './webhook-manager.js';
7
+ export type {
8
+ AdminActivityEvent,
9
+ AdminConnectionSnapshot,
10
+ AdminHealthCheck,
11
+ AdminMessageSummary,
12
+ AdminProtocolSummary,
13
+ AdminServerStats,
14
+ AdminSubscriptionSnapshot,
15
+ AdminTenantDetail,
16
+ AdminTenantSummary,
17
+ AdminWebhook,
18
+ AdminWebhookInput,
19
+ GlobalStats,
20
+ HealthCheckResult,
21
+ PaginatedResponse,
22
+ RateLimitEntry,
23
+ RateLimitStatus,
24
+ RuntimeConfig,
25
+ RuntimeConfigPatch,
26
+ TenantExport,
27
+ TenantListOptions,
28
+ TenantQuota,
29
+ TenantQuotaInput,
30
+ TenantQuotaStatus,
31
+ TenantStats,
32
+ } from './types.js';
33
+ export type { AuditEvent, AuditEventInput, AuditQueryOptions, AuditRetentionConfig } from './audit-log.js';
34
+ export type { WebhookPayload } from './webhook-manager.js';
@@ -0,0 +1,352 @@
1
+ /**
2
+ * Options for listing tenants with search, filter, and sort capabilities.
3
+ *
4
+ * @see https://github.com/enboxorg/enbox/issues/390
5
+ */
6
+ export type TenantListOptions = {
7
+ /** Cursor for keyset pagination. */
8
+ cursor? : string;
9
+ /** Maximum number of results per page. */
10
+ limit? : number;
11
+ /** Substring search across tenant DIDs. */
12
+ search? : string;
13
+ /** Filter by suspension status. */
14
+ status? : 'active' | 'suspended';
15
+ /** Sort field. Default: `did`. */
16
+ sort? : 'did' | 'storage' | 'messages';
17
+ /** Sort direction. Default: `asc`. */
18
+ order? : 'asc' | 'desc';
19
+ };
20
+
21
+ /**
22
+ * Summary of a tenant for list endpoints.
23
+ */
24
+ export type AdminTenantSummary = {
25
+ did : string;
26
+ messageCount : number;
27
+ dataStorageBytes : number;
28
+ };
29
+
30
+ /**
31
+ * Detailed information about a single tenant.
32
+ */
33
+ export type AdminTenantDetail = {
34
+ did : string;
35
+ isActive : boolean;
36
+ suspended : boolean;
37
+ registration : {
38
+ termsOfServiceHash? : string;
39
+ } | undefined;
40
+ storage : {
41
+ messageCount : number;
42
+ dataStorageBytes : number;
43
+ protocolCount : number;
44
+ };
45
+ protocols : string[];
46
+ quota? : {
47
+ maxMessages : number;
48
+ maxStorageBytes : number;
49
+ source : 'tenant' | 'global' | 'unlimited';
50
+ };
51
+ };
52
+
53
+ /**
54
+ * Per-tenant storage and usage statistics.
55
+ */
56
+ export type TenantStats = {
57
+ messageCount : number;
58
+ dataStorageBytes : number;
59
+ protocolCount : number;
60
+ protocols : string[];
61
+ };
62
+
63
+ /**
64
+ * Global server statistics.
65
+ */
66
+ export type GlobalStats = {
67
+ tenantCount : number;
68
+ totalMessages : number;
69
+ totalDataBytes : number;
70
+ totalProtocols : number;
71
+ };
72
+
73
+ /**
74
+ * Health check result for a single backend component.
75
+ */
76
+ export type HealthCheckResult = {
77
+ status : 'healthy' | 'unhealthy';
78
+ latencyMs : number;
79
+ error? : string;
80
+ };
81
+
82
+ /**
83
+ * Full deep health check response.
84
+ */
85
+ export type AdminHealthCheck = {
86
+ status : 'healthy' | 'unhealthy';
87
+ uptime : number;
88
+ version : string | undefined;
89
+ checks : Record<string, HealthCheckResult>;
90
+ };
91
+
92
+ /**
93
+ * Server statistics response.
94
+ */
95
+ export type AdminServerStats = {
96
+ tenants : {
97
+ total : number;
98
+ suspended : number;
99
+ };
100
+ storage : {
101
+ totalMessages : number;
102
+ totalDataBytes : number;
103
+ totalProtocols : number;
104
+ };
105
+ connections : {
106
+ websocket : {
107
+ active : number;
108
+ subscriptions : number;
109
+ };
110
+ };
111
+ registration : {
112
+ proofOfWorkEnabled : boolean;
113
+ };
114
+ uptime : number;
115
+ };
116
+
117
+ /**
118
+ * Paginated response wrapper.
119
+ */
120
+ export type PaginatedResponse<T> = {
121
+ data : T[];
122
+ cursor? : string;
123
+ totalCount : number;
124
+ };
125
+
126
+ /**
127
+ * A single DWN activity event captured by the in-memory ring buffer.
128
+ */
129
+ export type AdminActivityEvent = {
130
+ /** Monotonically increasing event ID (used as cursor). */
131
+ id : number;
132
+ /** ISO-8601 timestamp of the event. */
133
+ timestamp : string;
134
+ /** The tenant DID the request targeted. */
135
+ tenant : string;
136
+ /** DWN interface (e.g. `Records`, `Protocols`, `Events`). */
137
+ interface : string;
138
+ /** DWN method (e.g. `Write`, `Query`, `Read`, `Subscribe`). */
139
+ method : string;
140
+ /** DWN status code returned (e.g. 200, 202, 401). */
141
+ statusCode : number;
142
+ /** Transport used (`http` or `ws`). */
143
+ transport : 'http' | 'ws';
144
+ /** Data size in bytes (if applicable). */
145
+ dataSizeBytes? : number;
146
+ };
147
+
148
+ /**
149
+ * Snapshot of a single subscription's flow control state.
150
+ */
151
+ export type AdminSubscriptionSnapshot = {
152
+ /** Subscription JSON-RPC ID. */
153
+ id : string | number;
154
+ /** Number of events sent but not yet acknowledged. */
155
+ inflight : number;
156
+ /** Number of events buffered waiting for the window to open. */
157
+ buffered : number;
158
+ };
159
+
160
+ /**
161
+ * Snapshot of a single WebSocket connection for the admin inspector.
162
+ */
163
+ export type AdminConnectionSnapshot = {
164
+ /** Unique connection identifier. */
165
+ id : string;
166
+ /** ISO-8601 timestamp when the connection was established. */
167
+ connectedAt : string;
168
+ /** Number of active subscriptions on this connection. */
169
+ subscriptionCount : number;
170
+ /** Per-subscription flow control state. */
171
+ subscriptions : AdminSubscriptionSnapshot[];
172
+ };
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // Per-tenant storage quotas
176
+ // ---------------------------------------------------------------------------
177
+
178
+ /**
179
+ * Per-tenant storage quota limits.
180
+ * A value of 0 (or `undefined`) means the global default applies.
181
+ */
182
+ export type TenantQuota = {
183
+ /** Tenant DID this quota applies to. */
184
+ did : string;
185
+ /** Maximum number of messages the tenant may store (0 = use global default). */
186
+ maxMessages : number;
187
+ /** Maximum data storage in bytes (0 = use global default). */
188
+ maxStorageBytes : number;
189
+ };
190
+
191
+ /**
192
+ * Request body for setting a tenant quota via the admin API.
193
+ */
194
+ export type TenantQuotaInput = {
195
+ maxMessages? : number;
196
+ maxStorageBytes? : number;
197
+ };
198
+
199
+ /**
200
+ * Current tenant usage relative to their quota.
201
+ */
202
+ export type TenantQuotaStatus = {
203
+ quota : {
204
+ maxMessages : number;
205
+ maxStorageBytes : number;
206
+ source : 'tenant' | 'global' | 'unlimited';
207
+ };
208
+ usage : {
209
+ messageCount : number;
210
+ storageBytes : number;
211
+ };
212
+ };
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // Rate limiting
216
+ // ---------------------------------------------------------------------------
217
+
218
+ /**
219
+ * Summary of rate limit state for a single key (IP or tenant DID).
220
+ */
221
+ export type RateLimitEntry = {
222
+ key : string;
223
+ tokens : number;
224
+ maxTokens : number;
225
+ refillRate : number;
226
+ };
227
+
228
+ /**
229
+ * Admin response for the rate-limits endpoint.
230
+ */
231
+ export type RateLimitStatus = {
232
+ config : {
233
+ perIp : {
234
+ requestsPerSecond : number;
235
+ burst : number;
236
+ enabled : boolean;
237
+ };
238
+ perTenant : {
239
+ requestsPerSecond : number;
240
+ burst : number;
241
+ enabled : boolean;
242
+ };
243
+ };
244
+ activeEntries : {
245
+ ip : number;
246
+ tenant : number;
247
+ };
248
+ };
249
+
250
+ // ---------------------------------------------------------------------------
251
+ // Runtime configuration
252
+ // ---------------------------------------------------------------------------
253
+
254
+ /**
255
+ * Runtime-changeable server settings (subset of DwnServerConfig).
256
+ */
257
+ export type RuntimeConfig = {
258
+ logLevel : string;
259
+ maxRecordDataSize : number;
260
+ maxInFlight : number;
261
+ quotaMaxMessages : number;
262
+ quotaMaxStorageBytes : number;
263
+ rateLimitRequestsPerSecond : number;
264
+ rateLimitBurst : number;
265
+ rateLimitTenantRequestsPerSecond : number;
266
+ rateLimitTenantBurst : number;
267
+ };
268
+
269
+ /**
270
+ * Input for PATCH /admin/api/config. All fields are optional.
271
+ */
272
+ export type RuntimeConfigPatch = Partial<RuntimeConfig>;
273
+
274
+ // ---------------------------------------------------------------------------
275
+ // Tenant data browser
276
+ // ---------------------------------------------------------------------------
277
+
278
+ /**
279
+ * Metadata-only view of a DWN message for the tenant data browser.
280
+ * Does not include decrypted content or encoded message bytes.
281
+ */
282
+ export type AdminMessageSummary = {
283
+ messageCid : string;
284
+ interface : string | null;
285
+ method : string | null;
286
+ protocol : string | null;
287
+ protocolPath : string | null;
288
+ schema : string | null;
289
+ dataFormat : string | null;
290
+ dataSize : number | null;
291
+ dateCreated : string | null;
292
+ messageTimestamp : string | null;
293
+ };
294
+
295
+ /**
296
+ * Protocol summary with record count per protocol.
297
+ */
298
+ export type AdminProtocolSummary = {
299
+ protocol : string;
300
+ messageCount : number;
301
+ };
302
+
303
+ // ---------------------------------------------------------------------------
304
+ // Tenant data export
305
+ // ---------------------------------------------------------------------------
306
+
307
+ /**
308
+ * Complete tenant data export for backup/migration.
309
+ *
310
+ * @see https://github.com/enboxorg/enbox/issues/391
311
+ */
312
+ export type TenantExport = {
313
+ metadata : {
314
+ tenant : string;
315
+ exportedAt : string;
316
+ messageCount : number;
317
+ dataRecordCount : number;
318
+ };
319
+ messages : Record<string, unknown>[];
320
+ dataRecords : Record<string, unknown>[];
321
+ };
322
+
323
+ // ---------------------------------------------------------------------------
324
+ // Webhook / Alerting
325
+ // ---------------------------------------------------------------------------
326
+
327
+ /**
328
+ * A registered webhook endpoint for admin event notifications.
329
+ *
330
+ * @see https://github.com/enboxorg/enbox/issues/395
331
+ */
332
+ export type AdminWebhook = {
333
+ /** Auto-generated webhook ID. */
334
+ id : string;
335
+ /** URL to POST events to. */
336
+ url : string;
337
+ /** Event patterns to subscribe to (e.g. `tenant.*`, `quota.warning`). Asterisk is wildcard. */
338
+ events : string[];
339
+ /** Optional shared secret for HMAC-SHA256 signature verification. */
340
+ secret? : string;
341
+ /** ISO-8601 timestamp of creation. */
342
+ createdAt : string;
343
+ };
344
+
345
+ /**
346
+ * Input for creating a webhook.
347
+ */
348
+ export type AdminWebhookInput = {
349
+ url : string;
350
+ events : string[];
351
+ secret? : string;
352
+ };