@learncard/network-brain-service 3.17.0 → 3.19.0

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 (274) hide show
  1. package/.env.example +4 -0
  2. package/AGENTS.md +177 -159
  3. package/CHANGELOG.md +148 -0
  4. package/README.md +26 -8
  5. package/compose.inbox.yml +7 -0
  6. package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
  7. package/dist/accesslayer/auth-grant/read.d.ts +1 -0
  8. package/dist/accesslayer/auth-grant/read.d.ts.map +1 -1
  9. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +3 -2
  10. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  11. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +1 -0
  12. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  13. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +5 -3
  14. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  15. package/dist/accesslayer/credential/create.d.ts +2 -1
  16. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  17. package/dist/accesslayer/credential/create.test.d.ts +2 -0
  18. package/dist/accesslayer/credential/create.test.d.ts.map +1 -0
  19. package/dist/accesslayer/credential/relationships/read.d.ts +1 -0
  20. package/dist/accesslayer/credential/relationships/read.d.ts.map +1 -1
  21. package/dist/accesslayer/credential-activity/create.d.ts.map +1 -1
  22. package/dist/accesslayer/credential-refresh/email-delivery.d.ts +18 -0
  23. package/dist/accesslayer/credential-refresh/email-delivery.d.ts.map +1 -0
  24. package/dist/accesslayer/credential-refresh/index.d.ts +1 -0
  25. package/dist/accesslayer/credential-refresh/index.d.ts.map +1 -1
  26. package/dist/accesslayer/credential-refresh/read.d.ts.map +1 -1
  27. package/dist/accesslayer/credential-refresh/update.d.ts.map +1 -1
  28. package/dist/accesslayer/inbox-batch/store.d.ts +33 -0
  29. package/dist/accesslayer/inbox-batch/store.d.ts.map +1 -0
  30. package/dist/accesslayer/inbox-credential/create.d.ts +5 -0
  31. package/dist/accesslayer/inbox-credential/create.d.ts.map +1 -1
  32. package/dist/accesslayer/inbox-credential/refresh.d.ts +71 -0
  33. package/dist/accesslayer/inbox-credential/refresh.d.ts.map +1 -0
  34. package/dist/accesslayer/inbox-credential/update.d.ts +1 -1
  35. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  36. package/dist/cache/atomic-counter.test-helpers.d.ts +3 -0
  37. package/dist/cache/atomic-counter.test-helpers.d.ts.map +1 -0
  38. package/dist/cache/atomic-counter.test.d.ts +2 -0
  39. package/dist/cache/atomic-counter.test.d.ts.map +1 -0
  40. package/dist/cache/index.d.ts +1 -1
  41. package/dist/cache/index.d.ts.map +1 -1
  42. package/dist/config/environment.d.ts +46 -0
  43. package/dist/config/environment.d.ts.map +1 -1
  44. package/dist/credential-refresh.d.ts.map +1 -1
  45. package/dist/didkit_wasm_bg.wasm +0 -0
  46. package/dist/helpers/activity.helpers.d.ts +4 -0
  47. package/dist/helpers/activity.helpers.d.ts.map +1 -1
  48. package/dist/helpers/boost-storage.helpers.test.d.ts +2 -0
  49. package/dist/helpers/boost-storage.helpers.test.d.ts.map +1 -0
  50. package/dist/helpers/boost.helpers.d.ts +3 -7
  51. package/dist/helpers/boost.helpers.d.ts.map +1 -1
  52. package/dist/helpers/contact-method.helpers.d.ts.map +1 -1
  53. package/dist/helpers/contact-method.helpers.test.d.ts +2 -0
  54. package/dist/helpers/contact-method.helpers.test.d.ts.map +1 -0
  55. package/dist/helpers/credential-refresh-email-content.helpers.d.ts +44 -0
  56. package/dist/helpers/credential-refresh-email-content.helpers.d.ts.map +1 -0
  57. package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts +2 -0
  58. package/dist/helpers/credential-refresh-email-content.helpers.test.d.ts.map +1 -0
  59. package/dist/helpers/credential-refresh-email.helpers.d.ts +17 -0
  60. package/dist/helpers/credential-refresh-email.helpers.d.ts.map +1 -0
  61. package/dist/helpers/credential-refresh-proof.helpers.d.ts +4 -0
  62. package/dist/helpers/credential-refresh-proof.helpers.d.ts.map +1 -0
  63. package/dist/helpers/credential-refresh-proof.helpers.test.d.ts +2 -0
  64. package/dist/helpers/credential-refresh-proof.helpers.test.d.ts.map +1 -0
  65. package/dist/helpers/credential-refresh.helpers.d.ts +45 -2
  66. package/dist/helpers/credential-refresh.helpers.d.ts.map +1 -1
  67. package/dist/helpers/credential.helpers.d.ts +4 -2
  68. package/dist/helpers/credential.helpers.d.ts.map +1 -1
  69. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  70. package/dist/helpers/inbox-batch-http.helpers.d.ts +15 -0
  71. package/dist/helpers/inbox-batch-http.helpers.d.ts.map +1 -0
  72. package/dist/helpers/inbox-batch-http.helpers.test.d.ts +2 -0
  73. package/dist/helpers/inbox-batch-http.helpers.test.d.ts.map +1 -0
  74. package/dist/helpers/inbox-batch-status.helpers.d.ts +3 -0
  75. package/dist/helpers/inbox-batch-status.helpers.d.ts.map +1 -0
  76. package/dist/helpers/inbox-batch.helpers.d.ts +10 -0
  77. package/dist/helpers/inbox-batch.helpers.d.ts.map +1 -0
  78. package/dist/helpers/inbox-batch.helpers.test.d.ts +2 -0
  79. package/dist/helpers/inbox-batch.helpers.test.d.ts.map +1 -0
  80. package/dist/helpers/inbox-issuance-error.helpers.d.ts +13 -0
  81. package/dist/helpers/inbox-issuance-error.helpers.d.ts.map +1 -0
  82. package/dist/helpers/inbox-queue.helpers.d.ts +11 -0
  83. package/dist/helpers/inbox-queue.helpers.d.ts.map +1 -0
  84. package/dist/helpers/inbox-queue.helpers.test.d.ts +2 -0
  85. package/dist/helpers/inbox-queue.helpers.test.d.ts.map +1 -0
  86. package/dist/helpers/inbox-refresh.helpers.d.ts +186 -0
  87. package/dist/helpers/inbox-refresh.helpers.d.ts.map +1 -0
  88. package/dist/helpers/inbox.helpers.d.ts +9 -1
  89. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  90. package/dist/helpers/issuedCredentialStatus.helpers.d.ts +5 -0
  91. package/dist/helpers/issuedCredentialStatus.helpers.d.ts.map +1 -0
  92. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  93. package/dist/helpers/rateLimit.helpers.d.ts.map +1 -1
  94. package/dist/helpers/refresh-send-intent.helpers.d.ts +51 -0
  95. package/dist/helpers/refresh-send-intent.helpers.d.ts.map +1 -0
  96. package/dist/helpers/shim.d.ts +1 -1
  97. package/dist/helpers/shim.d.ts.map +1 -1
  98. package/dist/helpers/signingAuthority.helpers.d.ts +3 -2
  99. package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
  100. package/dist/helpers/signingAuthority.helpers.test.d.ts +2 -0
  101. package/dist/helpers/signingAuthority.helpers.test.d.ts.map +1 -0
  102. package/dist/helpers/status-list.helpers.d.ts.map +1 -1
  103. package/dist/helpers/status-list.helpers.test.d.ts +2 -0
  104. package/dist/helpers/status-list.helpers.test.d.ts.map +1 -0
  105. package/dist/inbox-queue-local.d.ts +2 -0
  106. package/dist/inbox-queue-local.d.ts.map +1 -0
  107. package/dist/index.js +2 -2
  108. package/dist/models/AuthGrant.d.ts.map +1 -1
  109. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  110. package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
  111. package/dist/models/Credential.d.ts +1 -0
  112. package/dist/models/Credential.d.ts.map +1 -1
  113. package/dist/models/CredentialActivity.d.ts +1 -0
  114. package/dist/models/CredentialActivity.d.ts.map +1 -1
  115. package/dist/models/CredentialRefresh.d.ts +5 -2
  116. package/dist/models/CredentialRefresh.d.ts.map +1 -1
  117. package/dist/models/InboxCredential.d.ts +1 -0
  118. package/dist/models/InboxCredential.d.ts.map +1 -1
  119. package/dist/models/credential-refresh-constraints.d.ts.map +1 -1
  120. package/dist/openapi.d.ts.map +1 -1
  121. package/dist/routes/act-as.test.d.ts +2 -0
  122. package/dist/routes/act-as.test.d.ts.map +1 -0
  123. package/dist/routes/activity.d.ts +3 -0
  124. package/dist/routes/activity.d.ts.map +1 -1
  125. package/dist/routes/app-store.d.ts.map +1 -1
  126. package/dist/routes/auth-grants.d.ts +10 -0
  127. package/dist/routes/auth-grants.d.ts.map +1 -1
  128. package/dist/routes/boosts.d.ts +506 -12
  129. package/dist/routes/boosts.d.ts.map +1 -1
  130. package/dist/routes/contracts.d.ts +48 -2
  131. package/dist/routes/contracts.d.ts.map +1 -1
  132. package/dist/routes/credential-refreshes.d.ts +4 -4
  133. package/dist/routes/credential-refreshes.d.ts.map +1 -1
  134. package/dist/routes/credentials.d.ts +8 -2
  135. package/dist/routes/credentials.d.ts.map +1 -1
  136. package/dist/routes/did-metadata.d.ts +8 -8
  137. package/dist/routes/federation.d.ts +2 -2
  138. package/dist/routes/inbox.d.ts +973 -31
  139. package/dist/routes/inbox.d.ts.map +1 -1
  140. package/dist/routes/index.d.ts +47 -127
  141. package/dist/routes/index.d.ts.map +1 -1
  142. package/dist/routes/presentations.d.ts +6 -6
  143. package/dist/routes/storage.d.ts +63 -63
  144. package/dist/routes/test.d.ts +6 -0
  145. package/dist/routes/test.d.ts.map +1 -1
  146. package/dist/routes/workflows.d.ts +14 -14
  147. package/dist/routes/workflows.d.ts.map +1 -1
  148. package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
  149. package/dist/services/delivery/adapters/postmark.adapter.test.d.ts +2 -0
  150. package/dist/services/delivery/adapters/postmark.adapter.test.d.ts.map +1 -0
  151. package/dist/services/registry/registry.factory.d.ts.map +1 -1
  152. package/dist/types/activity.d.ts +4 -0
  153. package/dist/types/activity.d.ts.map +1 -1
  154. package/dist/types/consentflowcontract.d.ts +22 -8
  155. package/dist/types/consentflowcontract.d.ts.map +1 -1
  156. package/dist/types/credential-refresh.d.ts +10 -1
  157. package/dist/types/credential-refresh.d.ts.map +1 -1
  158. package/dist/types/credential.d.ts +7 -0
  159. package/dist/types/credential.d.ts.map +1 -1
  160. package/dist/types/did-metadata.d.ts +2 -2
  161. package/dist/types/inbox-batch.d.ts +38 -0
  162. package/dist/types/inbox-batch.d.ts.map +1 -0
  163. package/inbox-e2e-setup.ts +53 -1
  164. package/inbox-elasticmq.conf +10 -0
  165. package/lambda.ts +61 -16
  166. package/package.json +18 -16
  167. package/scripts/setup-inbox-queues.ts +60 -0
  168. package/serverless-local.yml +2 -0
  169. package/serverless.yml +75 -0
  170. package/src/accesslayer/app-store-listing/relationships/read.ts +4 -3
  171. package/src/accesslayer/auth-grant/read.ts +2 -1
  172. package/src/accesslayer/consentflowcontract/relationships/create.ts +15 -5
  173. package/src/accesslayer/consentflowcontract/relationships/read.ts +161 -63
  174. package/src/accesslayer/consentflowcontract/relationships/update.ts +43 -10
  175. package/src/accesslayer/credential/create.test.ts +141 -0
  176. package/src/accesslayer/credential/create.ts +27 -3
  177. package/src/accesslayer/credential-activity/create.ts +1 -0
  178. package/src/accesslayer/credential-refresh/email-delivery.ts +116 -0
  179. package/src/accesslayer/credential-refresh/index.ts +1 -0
  180. package/src/accesslayer/credential-refresh/read.ts +8 -2
  181. package/src/accesslayer/credential-refresh/update.ts +4 -0
  182. package/src/accesslayer/inbox-batch/store.ts +469 -0
  183. package/src/accesslayer/inbox-credential/create.ts +51 -19
  184. package/src/accesslayer/inbox-credential/refresh.ts +221 -0
  185. package/src/accesslayer/inbox-credential/update.ts +1 -1
  186. package/src/cache/atomic-counter.test-helpers.ts +27 -0
  187. package/src/cache/atomic-counter.test.ts +6 -0
  188. package/src/cache/index.ts +25 -13
  189. package/src/config/environment.ts +26 -0
  190. package/src/credential-refresh.ts +5 -1
  191. package/src/docker-entry.ts +8 -0
  192. package/src/helpers/activity.helpers.ts +4 -0
  193. package/src/helpers/app-counter.helpers.ts +1 -1
  194. package/src/helpers/boost-storage.helpers.test.ts +242 -0
  195. package/src/helpers/boost.helpers.ts +58 -437
  196. package/src/helpers/contact-method.helpers.test.ts +43 -0
  197. package/src/helpers/contact-method.helpers.ts +3 -4
  198. package/src/helpers/credential-refresh-email-content.helpers.test.ts +222 -0
  199. package/src/helpers/credential-refresh-email-content.helpers.ts +155 -0
  200. package/src/helpers/credential-refresh-email.helpers.ts +201 -0
  201. package/src/helpers/credential-refresh-proof.helpers.test.ts +103 -0
  202. package/src/helpers/credential-refresh-proof.helpers.ts +43 -0
  203. package/src/helpers/credential-refresh.helpers.ts +385 -107
  204. package/src/helpers/credential.helpers.ts +6 -2
  205. package/src/helpers/finalize-inbox.helpers.ts +39 -22
  206. package/src/helpers/inbox-batch-http.helpers.test.ts +188 -0
  207. package/src/helpers/inbox-batch-http.helpers.ts +150 -0
  208. package/src/helpers/inbox-batch-status.helpers.ts +11 -0
  209. package/src/helpers/inbox-batch.helpers.test.ts +668 -0
  210. package/src/helpers/inbox-batch.helpers.ts +374 -0
  211. package/src/helpers/inbox-issuance-error.helpers.ts +25 -0
  212. package/src/helpers/inbox-queue.helpers.test.ts +253 -0
  213. package/src/helpers/inbox-queue.helpers.ts +372 -0
  214. package/src/helpers/inbox-refresh.helpers.ts +432 -0
  215. package/src/helpers/inbox.helpers.ts +301 -50
  216. package/src/helpers/issuedCredentialStatus.helpers.ts +11 -0
  217. package/src/helpers/notifications.helpers.ts +4 -2
  218. package/src/helpers/rateLimit.helpers.ts +2 -5
  219. package/src/helpers/refresh-send-intent.helpers.ts +273 -0
  220. package/src/helpers/shim.ts +32 -31
  221. package/src/helpers/signingAuthority.helpers.test.ts +198 -0
  222. package/src/helpers/signingAuthority.helpers.ts +41 -18
  223. package/src/helpers/status-list.helpers.test.ts +77 -0
  224. package/src/helpers/status-list.helpers.ts +31 -8
  225. package/src/inbox-queue-local.ts +28 -0
  226. package/src/models/AuthGrant.ts +1 -0
  227. package/src/models/ConsentFlowTerms.ts +4 -0
  228. package/src/models/ConsentFlowTransaction.ts +4 -0
  229. package/src/models/Credential.ts +1 -0
  230. package/src/models/CredentialActivity.ts +3 -5
  231. package/src/models/CredentialRefresh.ts +4 -2
  232. package/src/models/InboxCredential.ts +2 -0
  233. package/src/models/credential-refresh-constraints.test.ts +8 -3
  234. package/src/models/credential-refresh-constraints.ts +5 -0
  235. package/src/openapi.ts +8 -0
  236. package/src/routes/act-as.test.ts +239 -0
  237. package/src/routes/app-store.ts +69 -52
  238. package/src/routes/auth-grants.ts +13 -11
  239. package/src/routes/boosts.ts +943 -120
  240. package/src/routes/contracts.ts +55 -7
  241. package/src/routes/credential-refreshes.test.ts +8 -2
  242. package/src/routes/credential-refreshes.ts +4 -1
  243. package/src/routes/credentials.ts +2 -1
  244. package/src/routes/inbox.ts +95 -49
  245. package/src/routes/index.ts +164 -88
  246. package/src/routes/skills.ts +1 -1
  247. package/src/routes/workflows.ts +80 -31
  248. package/src/services/delivery/adapters/postmark.adapter.test.ts +54 -0
  249. package/src/services/delivery/adapters/postmark.adapter.ts +12 -0
  250. package/src/services/registry/registry.factory.ts +2 -1
  251. package/src/types/activity.ts +2 -0
  252. package/src/types/consentflowcontract.ts +21 -3
  253. package/src/types/credential-refresh.ts +21 -2
  254. package/src/types/credential.ts +13 -1
  255. package/src/types/inbox-batch.ts +46 -0
  256. package/test/app-store-listing.spec.ts +66 -30
  257. package/test/atomic-counter.e2e.ts +12 -0
  258. package/test/auth-grant.spec.ts +21 -21
  259. package/test/bitstring-status-list.spec.ts +12 -9
  260. package/test/consentflow.spec.ts +453 -26
  261. package/test/credential-refresh-email.spec.ts +663 -0
  262. package/test/credential-refresh-publication.spec.ts +13 -5
  263. package/test/guardian-gated-routes.spec.ts +337 -180
  264. package/test/helpers/inbox-e2e-environment.ts +6 -0
  265. package/test/inbox-batch.spec.ts +1338 -0
  266. package/test/inbox-refresh.spec.ts +534 -0
  267. package/test/inbox-security.e2e.ts +48 -3
  268. package/test/openapi.spec.ts +41 -1
  269. package/test/refresh-send.spec.ts +1799 -0
  270. package/test/registry.spec.ts +56 -11
  271. package/test/revoke-suspend-per-instance.spec.ts +9 -1
  272. package/vitest.config.ts +13 -0
  273. package/vitest.inbox-e2e.config.ts +5 -1
  274. package/vitest.integration.config.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"credential-refresh.d.ts","sourceRoot":"","sources":["../../src/types/credential-refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,+BAA+B;;;;EAAkD,CAAC;AAC/F,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAGrF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC3C,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAEvF,eAAO,MAAM,sCAAsC;;;;;;;;;;;;;;;iBAajD,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sCAAsC,CAAC,CAAC;AASnG,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BhD,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qCAAqC,CAAC,CAAC;AAGjG,eAAO,MAAM,uCAAuC;;;;;;;;;;;;;;;;;;;;;;;;;;iBAElD,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAChD,OAAO,uCAAuC,CACjD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqBtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;EAOtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;;;;;;iBAKtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC"}
1
+ {"version":3,"file":"credential-refresh.d.ts","sourceRoot":"","sources":["../../src/types/credential-refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,+BAA+B;;;;;EAK1C,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAGrF,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkD3C,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAEvF,eAAO,MAAM,sCAAsC;;;;;;;;;;;;;;;iBAajD,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sCAAsC,CAAC,CAAC;AASnG,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BhD,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qCAAqC,CAAC,CAAC;AAGjG,eAAO,MAAM,uCAAuC;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAElD,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAChD,OAAO,uCAAuC,CACjD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuBtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;EAOtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC;AAEF,eAAO,MAAM,2CAA2C;;;;;;;;;iBAKtD,CAAC;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,KAAK,CACpD,OAAO,2CAA2C,CACrD,CAAC"}
@@ -1,7 +1,14 @@
1
1
  import { z } from 'zod';
2
+ import type { BitstringStatusListEntry, JWE, VC } from '@learncard/types';
3
+ export interface IssuedCredential {
4
+ readonly kind: 'issued-credential';
5
+ readonly credential: VC | JWE;
6
+ readonly statusEntries: BitstringStatusListEntry[];
7
+ }
2
8
  export declare const CredentialValidator: z.ZodObject<{
3
9
  id: z.ZodString;
4
10
  credential: z.ZodString;
11
+ statusEntries: z.ZodOptional<z.ZodString>;
5
12
  }, z.core.$strip>;
6
13
  export type CredentialType = z.infer<typeof CredentialValidator>;
7
14
  //# sourceMappingURL=credential.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../src/types/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,mBAAmB;;;iBAAuD,CAAC;AACxF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
1
+ {"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../../src/types/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAG1E,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,EAAE,GAAG,GAAG,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,wBAAwB,EAAE,CAAC;CACtD;AAED,eAAO,MAAM,mBAAmB;;;;iBAI9B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -129,7 +129,7 @@ export declare const DidMetadataValidator: z.ZodObject<{
129
129
  }, z.core.$catchall<z.ZodAny>>>>>;
130
130
  proof: z.ZodOptional<z.ZodOptional<z.ZodUnion<[z.ZodObject<{
131
131
  type: z.ZodString;
132
- created: z.ZodString;
132
+ created: z.ZodOptional<z.ZodString>;
133
133
  challenge: z.ZodOptional<z.ZodString>;
134
134
  domain: z.ZodOptional<z.ZodString>;
135
135
  nonce: z.ZodOptional<z.ZodString>;
@@ -138,7 +138,7 @@ export declare const DidMetadataValidator: z.ZodObject<{
138
138
  jws: z.ZodOptional<z.ZodString>;
139
139
  }, z.core.$catchall<z.ZodAny>>, z.ZodArray<z.ZodObject<{
140
140
  type: z.ZodString;
141
- created: z.ZodString;
141
+ created: z.ZodOptional<z.ZodString>;
142
142
  challenge: z.ZodOptional<z.ZodString>;
143
143
  domain: z.ZodOptional<z.ZodString>;
144
144
  nonce: z.ZodOptional<z.ZodString>;
@@ -0,0 +1,38 @@
1
+ import type { InboxBatchStatus, IssueInboxCredentialBatch } from '@learncard/types';
2
+ import type { Context } from '.././routes/index';
3
+ export type BatchItem = {
4
+ id: string;
5
+ batchId: string;
6
+ index: number;
7
+ state: InboxBatchStatus['items'][number]['state'];
8
+ phase?: 'PREPARING' | 'ISSUING';
9
+ owner?: string;
10
+ leaseUntil?: number;
11
+ attempts: number;
12
+ result?: string;
13
+ replayKey: string;
14
+ duplicate: boolean;
15
+ correlation?: string;
16
+ };
17
+ export type InboxDispatchLease = {
18
+ id: string;
19
+ dispatchAt: number;
20
+ };
21
+ export type BatchJob = {
22
+ id: string;
23
+ issuer: string;
24
+ createdAt: number;
25
+ payload?: string;
26
+ completedAt?: number;
27
+ };
28
+ export type BatchJobPayload = {
29
+ batch: IssueInboxCredentialBatch;
30
+ context: Pick<Context, 'domain' | 'tenant'>;
31
+ scope?: string;
32
+ };
33
+ export type BatchReplayStore = {
34
+ get: (key: string) => Promise<string | null | undefined>;
35
+ setIfAbsent: (key: string, value: string, ttl: number) => Promise<'OK' | null | undefined>;
36
+ compareAndSet: (key: string, expected: string, value: string | null, ttl: number) => Promise<boolean | undefined>;
37
+ };
38
+ //# sourceMappingURL=inbox-batch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox-batch.d.ts","sourceRoot":"","sources":["../../src/types/inbox-batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,MAAM,SAAS,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;IAClD,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IAEnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,QAAQ,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC1B,KAAK,EAAE,yBAAyB,CAAC;IACjC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;IAE5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC3B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACzD,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IAC3F,aAAa,EAAE,CACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,KACV,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;CACrC,CAAC"}
@@ -1,18 +1,70 @@
1
1
  import { Neo4jContainer } from '@testcontainers/neo4j';
2
2
  import { GenericContainer } from 'testcontainers';
3
3
  import type { TestProject } from 'vitest/node';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { SQSClient, CreateQueueCommand, GetQueueAttributesCommand } from '@aws-sdk/client-sqs';
4
6
 
5
7
  /** Random host ports and fresh containers keep these tests away from development data. */
6
8
  export default async function setup({ provide }: TestProject): Promise<() => Promise<void>> {
7
9
  const neo4j = await new Neo4jContainer('neo4j:5').start();
8
10
  try {
9
11
  const redis = await new GenericContainer('redis:7-alpine').withExposedPorts(6379).start();
12
+ let sqs: Awaited<ReturnType<GenericContainer['start']>> | undefined;
13
+ try {
14
+ sqs = await new GenericContainer('softwaremill/elasticmq-native:1.6.15')
15
+ .withCopyFilesToContainer([
16
+ {
17
+ source: fileURLToPath(new URL('./inbox-elasticmq.conf', import.meta.url)),
18
+ target: '/opt/elasticmq.conf',
19
+ },
20
+ ])
21
+ .withExposedPorts(9324)
22
+ .start();
23
+ const endpoint = `http://${sqs.getHost()}:${sqs.getMappedPort(9324)}`;
24
+ const client = new SQSClient({
25
+ endpoint,
26
+ region: 'us-east-1',
27
+ credentials: { accessKeyId: 'local', secretAccessKey: 'local' },
28
+ });
29
+ try {
30
+ const dlq = await client.send(
31
+ new CreateQueueCommand({ QueueName: 'inbox-e2e-dlq' })
32
+ );
33
+ const dlqUrl = `${endpoint}${new URL(dlq.QueueUrl!).pathname}`;
34
+ const attributes = await client.send(
35
+ new GetQueueAttributesCommand({
36
+ QueueUrl: dlqUrl,
37
+ AttributeNames: ['QueueArn'],
38
+ })
39
+ );
40
+ const queue = await client.send(
41
+ new CreateQueueCommand({
42
+ QueueName: 'inbox-e2e',
43
+ Attributes: {
44
+ VisibilityTimeout: '1800',
45
+ RedrivePolicy: JSON.stringify({
46
+ deadLetterTargetArn: attributes.Attributes!.QueueArn,
47
+ maxReceiveCount: '5',
48
+ }),
49
+ },
50
+ })
51
+ );
52
+ provide('inbox-queue-endpoint', endpoint);
53
+ provide('inbox-queue-url', `${endpoint}${new URL(queue.QueueUrl!).pathname}`);
54
+ provide('inbox-dead-letter-url', dlqUrl);
55
+ } finally {
56
+ client.destroy();
57
+ }
58
+ } catch (error) {
59
+ await Promise.all([redis.stop(), sqs?.stop()]);
60
+ throw error;
61
+ }
10
62
  provide('neo4j-uri', neo4j.getBoltUri());
11
63
  provide('neo4j-password', neo4j.getPassword());
12
64
  provide('inbox-redis-host', redis.getHost());
13
65
  provide('inbox-redis-port', redis.getMappedPort(6379));
14
66
  return async () => {
15
- await Promise.all([redis.stop(), neo4j.stop()]);
67
+ await Promise.all([redis.stop(), neo4j.stop(), sqs?.stop()]);
16
68
  };
17
69
  } catch (error) {
18
70
  await neo4j.stop();
@@ -0,0 +1,10 @@
1
+ include classpath("application.conf")
2
+ node-address { protocol = http, host = localhost, port = 9324, context-path = "" }
3
+ # Compose uses these queues; isolated tests create their own inbox-e2e queues dynamically.
4
+ queues {
5
+ inbox-dlq { defaultVisibilityTimeout = 180 seconds }
6
+ inbox {
7
+ defaultVisibilityTimeout = 1800 seconds
8
+ deadLettersQueue { name = "inbox-dlq", maxReceiveCount = 5 }
9
+ }
10
+ }
package/lambda.ts CHANGED
@@ -25,6 +25,10 @@ import {
25
25
  import { environment } from './src/config/environment';
26
26
  import { toServerlessApplication } from './src/helpers/serverlessApplication';
27
27
  import { runInboxMaintenance } from './src/helpers/inbox-maintenance.helpers';
28
+ import {
29
+ inboxBatchResponseMeta,
30
+ withInboxBatchBodyLimit,
31
+ } from './src/helpers/inbox-batch-http.helpers';
28
32
 
29
33
  Sentry.AWSLambda.init({
30
34
  dsn: environment.SENTRY_DSN,
@@ -61,8 +65,9 @@ export const credentialRefreshHandler: typeof credentialRefreshProxy = async (ev
61
65
 
62
66
  export const _openApiHandler = createOpenApiAwsLambdaHandler({
63
67
  router: appRouter,
64
- responseMeta: () => {
68
+ responseMeta: meta => {
65
69
  return {
70
+ ...inboxBatchResponseMeta(meta),
66
71
  headers: {
67
72
  'Access-Control-Allow-Origin': '*',
68
73
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
@@ -74,21 +79,24 @@ export const _openApiHandler = createOpenApiAwsLambdaHandler({
74
79
  onError: handleTrpcError,
75
80
  });
76
81
 
77
- export const _trpcHandler = awsLambdaRequestHandler({
78
- allowMethodOverride: true,
79
- router: appRouter,
80
- createContext,
81
- onError: handleTrpcError,
82
- responseMeta: () => {
83
- return {
84
- headers: {
85
- 'Access-Control-Allow-Origin': '*',
86
- 'Access-Control-Allow-Methods': '*',
87
- 'Access-Control-Allow-Headers': 'authorization',
88
- },
89
- };
90
- },
91
- });
82
+ export const _trpcHandler = withInboxBatchBodyLimit(
83
+ awsLambdaRequestHandler({
84
+ allowMethodOverride: true,
85
+ router: appRouter,
86
+ createContext,
87
+ onError: handleTrpcError,
88
+ responseMeta: () => {
89
+ return {
90
+ headers: {
91
+ 'Access-Control-Allow-Origin': '*',
92
+ 'Access-Control-Allow-Methods': '*',
93
+ 'Access-Control-Allow-Headers': 'authorization',
94
+ },
95
+ };
96
+ },
97
+ }),
98
+ 'trpc'
99
+ );
92
100
 
93
101
  export const openApiHandler = Sentry.AWSLambda.wrapHandler(
94
102
  async (event: APIGatewayProxyEventV2, context: Context): Promise<APIGatewayProxyResultV2> => {
@@ -124,6 +132,43 @@ export const trpcHandler = Sentry.AWSLambda.wrapHandler(
124
132
  }
125
133
  );
126
134
 
135
+ export const inboxQueueWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
136
+ const { processInboxQueueMessage } = await import('./src/helpers/inbox-queue.helpers');
137
+ const batchItemFailures = [];
138
+ for (const record of event.Records) {
139
+ try {
140
+ await processInboxQueueMessage(record.body);
141
+ } catch {
142
+ console.error('Inbox worker message failed', { messageId: record.messageId });
143
+ batchItemFailures.push({ itemIdentifier: record.messageId });
144
+ }
145
+ }
146
+ return { batchItemFailures } satisfies SQSBatchResponse;
147
+ });
148
+
149
+ export const inboxQueueDispatcher = Sentry.AWSLambda.wrapHandler(
150
+ async (_event: unknown, context: Context): Promise<void> => {
151
+ const { dispatchInboxJobs } = await import('./src/helpers/inbox-queue.helpers');
152
+ // Share one clock across publication and recovery, retaining five seconds for shutdown.
153
+ const deadline = Date.now() + Math.max(0, context.getRemainingTimeInMillis() - 5_000);
154
+ await dispatchInboxJobs(deadline);
155
+ }
156
+ );
157
+
158
+ export const inboxDeadLetterWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
159
+ const { processInboxDeadLetter } = await import('./src/helpers/inbox-queue.helpers');
160
+ const batchItemFailures = [];
161
+ for (const record of event.Records) {
162
+ try {
163
+ await processInboxDeadLetter(record.body);
164
+ } catch {
165
+ console.error('Inbox dead-letter processing failed', { messageId: record.messageId });
166
+ batchItemFailures.push({ itemIdentifier: record.messageId });
167
+ }
168
+ }
169
+ return { batchItemFailures } satisfies SQSBatchResponse;
170
+ });
171
+
127
172
  export const notificationsWorker: SQSHandler = Sentry.AWSLambda.wrapHandler(async event => {
128
173
  const batchItemFailures = await Promise.all(
129
174
  event.Records.map(async record => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@learncard/network-brain-service",
3
- "version": "3.17.0",
3
+ "version": "3.19.0",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=24.12 <25"
@@ -9,6 +9,8 @@
9
9
  "main": "dist/index.js",
10
10
  "types": "dist/index.d.ts",
11
11
  "scripts": {
12
+ "inbox:setup-queues": "bun scripts/setup-inbox-queues.ts",
13
+ "inbox:worker": "bun --conditions=development src/inbox-queue-local.ts",
12
14
  "test": "vitest run --config vitest.config.ts",
13
15
  "test:watch": "vitest --config vitest.config.ts",
14
16
  "test:coverage": "vitest run --coverage --config vitest.config.ts",
@@ -59,21 +61,21 @@
59
61
  "@digitalcredentials/issuer-registry-client": "^3.2.0-beta.5",
60
62
  "@fastify/cors": "^11.2.0",
61
63
  "@google/genai": "^1.27.0",
62
- "@learncard/core": "9.4.34",
63
- "@learncard/crypto-plugin": "1.1.34",
64
- "@learncard/did-web-plugin": "1.1.34",
65
- "@learncard/didkey-plugin": "1.1.34",
66
- "@learncard/didkit-plugin": "1.9.14",
67
- "@learncard/didkit-plugin-node": "0.2.32",
68
- "@learncard/dynamic-loader-plugin": "1.1.34",
69
- "@learncard/encryption-plugin": "1.1.34",
70
- "@learncard/expiration-plugin": "1.2.34",
71
- "@learncard/email-templates": "1.0.6",
72
- "@learncard/helpers": "1.5.0",
73
- "@learncard/learn-card-plugin": "1.2.34",
74
- "@learncard/types": "5.19.0",
75
- "@learncard/vc-plugin": "1.6.0",
76
- "@learncard/vc-templates-plugin": "1.1.34",
64
+ "@learncard/core": "9.4.36",
65
+ "@learncard/crypto-plugin": "1.1.36",
66
+ "@learncard/did-web-plugin": "1.1.36",
67
+ "@learncard/didkey-plugin": "1.1.36",
68
+ "@learncard/didkit-plugin": "1.10.1",
69
+ "@learncard/didkit-plugin-node": "0.3.1",
70
+ "@learncard/dynamic-loader-plugin": "1.1.36",
71
+ "@learncard/encryption-plugin": "1.1.36",
72
+ "@learncard/expiration-plugin": "1.2.36",
73
+ "@learncard/email-templates": "1.1.0",
74
+ "@learncard/helpers": "1.6.0",
75
+ "@learncard/learn-card-plugin": "1.2.36",
76
+ "@learncard/types": "5.21.0",
77
+ "@learncard/vc-plugin": "1.6.2",
78
+ "@learncard/vc-templates-plugin": "1.1.36",
77
79
  "@sentry/serverless": "7.61.0",
78
80
  "@trpc/server": "11.8.0",
79
81
  "@types/lodash": "^4.14.191",
@@ -0,0 +1,60 @@
1
+ import {
2
+ SQSClient,
3
+ CreateQueueCommand,
4
+ GetQueueUrlCommand,
5
+ GetQueueAttributesCommand,
6
+ SetQueueAttributesCommand,
7
+ } from '@aws-sdk/client-sqs';
8
+
9
+ const endpoint = process.env.INBOX_QUEUE_ENDPOINT ?? 'http://localhost:9324';
10
+ const client = new SQSClient({
11
+ region: process.env.AWS_REGION ?? 'us-east-1',
12
+ endpoint,
13
+ credentials: { accessKeyId: 'local', secretAccessKey: 'local' },
14
+ });
15
+
16
+ const ensureQueue = async (name: string): Promise<string> => {
17
+ let url: string | undefined;
18
+ try {
19
+ url = (await client.send(new GetQueueUrlCommand({ QueueName: name }))).QueueUrl;
20
+ } catch (error) {
21
+ if (
22
+ !(error instanceof Error) ||
23
+ !['QueueDoesNotExist', 'AWS.SimpleQueueService.NonExistentQueue'].includes(error.name)
24
+ )
25
+ throw error;
26
+ url = (await client.send(new CreateQueueCommand({ QueueName: name }))).QueueUrl;
27
+ }
28
+ if (!url) throw new Error(`Missing URL for ${name}`);
29
+ // ElasticMQ may advertise its container hostname; use the caller's reachable endpoint.
30
+ return `${endpoint.replace(/\/$/, '')}${new URL(url).pathname}`;
31
+ };
32
+
33
+ try {
34
+ const dlqUrl = await ensureQueue('inbox-dlq');
35
+ const attributes = await client.send(
36
+ new GetQueueAttributesCommand({
37
+ QueueUrl: dlqUrl,
38
+ AttributeNames: ['QueueArn'],
39
+ })
40
+ );
41
+ if (!attributes.Attributes?.QueueArn)
42
+ throw new Error('Inbox dead-letter queue ARN is missing.');
43
+ const queueUrl = await ensureQueue('inbox');
44
+ await client.send(
45
+ new SetQueueAttributesCommand({
46
+ QueueUrl: queueUrl,
47
+ Attributes: {
48
+ VisibilityTimeout: '1800',
49
+ RedrivePolicy: JSON.stringify({
50
+ deadLetterTargetArn: attributes.Attributes.QueueArn,
51
+ maxReceiveCount: '5',
52
+ }),
53
+ },
54
+ })
55
+ );
56
+ console.log(`INBOX_QUEUE_URL=${queueUrl}`);
57
+ console.log(`INBOX_DEAD_LETTER_QUEUE_URL=${dlqUrl}`);
58
+ } finally {
59
+ client.destroy();
60
+ }
@@ -47,6 +47,8 @@ provider:
47
47
  stage: ${opt:stage, "dev"}
48
48
  region: ${opt:region, "us-east-1"}
49
49
  environment:
50
+ INBOX_QUEUE_URL: ${env:INBOX_QUEUE_URL, ''}
51
+ INBOX_QUEUE_ENDPOINT: ${env:INBOX_QUEUE_ENDPOINT, ''}
50
52
  PORT: ${env:PORT, 3000}
51
53
  LAMBDA_STAGE: ${opt:stage, "dev"}
52
54
  SEED: ${env:SEED}
package/serverless.yml CHANGED
@@ -15,6 +15,20 @@ plugins:
15
15
  - serverless-prune-plugin
16
16
 
17
17
  constructs:
18
+ inbox-queue:
19
+ type: queue
20
+ batchSize: 1
21
+ maxConcurrency: 10
22
+ maxRetries: 5
23
+ worker:
24
+ handler: lambda.inboxQueueWorker
25
+ timeout: 300
26
+ reservedConcurrency: 10
27
+ vpc:
28
+ securityGroupIds:
29
+ - 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
30
+ subnetIds:
31
+ - Ref: PrivateSubnetA
18
32
  notifications-queue:
19
33
  type: queue
20
34
  worker:
@@ -128,6 +142,7 @@ provider:
128
142
  PORT: ${env:PORT, 3000}
129
143
  INBOX_DELETE_EXPIRED_RECORDS: ${env:INBOX_DELETE_EXPIRED_RECORDS, "false"}
130
144
  LAMBDA_STAGE: ${opt:stage, "dev"}
145
+ INBOX_BATCH_ITEMS_PER_HOUR: ${env:INBOX_BATCH_ITEMS_PER_HOUR, '10000'}
131
146
  SEED: ${env:SEED}
132
147
  DOMAIN_NAME: ${env:DOMAIN_NAME}
133
148
  CLIENT_APP_DOMAIN_NAME: ${env:CLIENT_APP_DOMAIN_NAME}
@@ -153,6 +168,7 @@ provider:
153
168
  REDIS_PORT:
154
169
  'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Port]
155
170
  NOTIFICATIONS_QUEUE_URL: ${construct:notifications-queue.queueUrl}
171
+ INBOX_QUEUE_URL: ${construct:inbox-queue.queueUrl}
156
172
  SKILL_EMBEDDING_GOOGLE_API_KEY: ${env:SKILL_EMBEDDING_GOOGLE_API_KEY, ""}
157
173
  SKILL_EMBEDDING_GOOGLE_MODEL: ${env:SKILL_EMBEDDING_GOOGLE_MODEL, "gemini-embedding-001"}
158
174
  SKILL_EMBEDDING_BATCH_SIZE: ${env:SKILL_EMBEDDING_BATCH_SIZE, "25"}
@@ -190,6 +206,31 @@ provider:
190
206
  - ETag
191
207
 
192
208
  functions:
209
+ inboxDeadLetterWorker:
210
+ handler: lambda.inboxDeadLetterWorker
211
+ timeout: 30
212
+ reservedConcurrency: 2
213
+ vpc:
214
+ securityGroupIds:
215
+ - 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
216
+ subnetIds:
217
+ - Ref: PrivateSubnetA
218
+ events:
219
+ - sqs:
220
+ arn: ${construct:inbox-queue.dlqArn}
221
+ batchSize: 1
222
+ functionResponseType: ReportBatchItemFailures
223
+ inboxQueueDispatcher:
224
+ handler: lambda.inboxQueueDispatcher
225
+ timeout: 60
226
+ reservedConcurrency: 1
227
+ vpc:
228
+ securityGroupIds:
229
+ - 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
230
+ subnetIds:
231
+ - Ref: PrivateSubnetA
232
+ events:
233
+ - schedule: rate(1 minute)
193
234
  inboxMaintenance:
194
235
  handler: lambda.inboxMaintenanceHandler
195
236
  timeout: 300
@@ -288,6 +329,40 @@ functions:
288
329
 
289
330
  resources:
290
331
  Resources:
332
+ InboxQueueAgeAlarm:
333
+ Type: AWS::CloudWatch::Alarm
334
+ Properties:
335
+ AlarmDescription: Inbox issuance has waited over five minutes
336
+ Namespace: AWS/SQS
337
+ MetricName: ApproximateAgeOfOldestMessage
338
+ Dimensions:
339
+ - Name: QueueName
340
+ Value:
341
+ Fn::Select:
342
+ - 4
343
+ - Fn::Split: ['/', '${construct:inbox-queue.queueUrl}']
344
+ Statistic: Maximum
345
+ Period: 60
346
+ EvaluationPeriods: 2
347
+ Threshold: 300
348
+ ComparisonOperator: GreaterThanThreshold
349
+ TreatMissingData: notBreaching
350
+ InboxDispatcherErrorsAlarm:
351
+ Type: AWS::CloudWatch::Alarm
352
+ Properties:
353
+ AlarmDescription: Durable inbox dispatch or recovery failed in three of five minutes
354
+ Namespace: AWS/Lambda
355
+ MetricName: Errors
356
+ Dimensions:
357
+ - Name: FunctionName
358
+ Value: { Ref: InboxQueueDispatcherLambdaFunction }
359
+ Statistic: Sum
360
+ Period: 60
361
+ EvaluationPeriods: 5
362
+ DatapointsToAlarm: 3
363
+ Threshold: 0
364
+ ComparisonOperator: GreaterThanThreshold
365
+ TreatMissingData: notBreaching
291
366
  VPC:
292
367
  Type: AWS::EC2::VPC
293
368
  Properties:
@@ -192,11 +192,12 @@ export const getCredentialsSentByListingToProfile = async (
192
192
  const isReceived = record.get('isReceived');
193
193
  const sentStatus = record.get('sentStatus');
194
194
  const receivedStatus = record.get('receivedStatus');
195
- const rawStatus = sentStatus ?? receivedStatus;
196
195
  let status: 'pending' | 'claimed' | 'revoked' | 'suspended' = 'pending';
197
196
 
198
- if (rawStatus === 'revoked' || rawStatus === 'suspended') {
199
- status = rawStatus;
197
+ if (sentStatus === 'revoked' || receivedStatus === 'revoked') {
198
+ status = 'revoked';
199
+ } else if (sentStatus === 'suspended' || receivedStatus === 'suspended') {
200
+ status = 'suspended';
200
201
  } else if (isReceived) {
201
202
  status = 'claimed';
202
203
  }
@@ -98,7 +98,7 @@ export const isAuthGrantAssociatedWithProfile = async (
98
98
  export const isAuthGrantChallengeValidForDID = async (
99
99
  challenge: string,
100
100
  did: string
101
- ): Promise<{ isChallengeValid: boolean; scope: string }> => {
101
+ ): Promise<{ isChallengeValid: boolean; scope: string; actAs?: string }> => {
102
102
  // TODO: Ideally, this transformation isn't required and we could use DID directly.
103
103
  const profileId = getProfileIdFromDid(did);
104
104
  if (!profileId) return { isChallengeValid: false, scope: AUTH_GRANT_NO_ACCESS_SCOPE };
@@ -126,5 +126,6 @@ export const isAuthGrantChallengeValidForDID = async (
126
126
  return {
127
127
  isChallengeValid: result.records.length > 0,
128
128
  scope: authGrant?.scope || AUTH_GRANT_NO_ACCESS_SCOPE,
129
+ actAs: authGrant?.actAs,
129
130
  };
130
131
  };
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  ConsentFlowTerms as ConsentFlowTermsType,
3
3
  ConsentFlowTransaction,
4
+ ConsentFlowGuardianApproval,
4
5
  LCNNotificationTypeEnumValidator,
5
6
  LCNProfile,
6
7
  UnsignedVC,
@@ -76,11 +77,13 @@ export const consentToContract = async (
76
77
  terms: _terms,
77
78
  expiresAt,
78
79
  oneTime,
80
+ guardianApproval,
79
81
  }: {
80
82
  terms: ConsentFlowTermsType;
81
83
  expiresAt?: string;
82
84
  liveSyncing?: boolean;
83
85
  oneTime?: boolean;
86
+ guardianApproval?: ConsentFlowGuardianApproval;
84
87
  },
85
88
  domain: string
86
89
  ) => {
@@ -122,7 +125,7 @@ export const consentToContract = async (
122
125
  if (existing.length > 0) {
123
126
  return reconsentTerms(
124
127
  { terms: inflateObject(existing[0]!.terms), consenter, contract, contractOwner },
125
- { terms, expiresAt, oneTime },
128
+ { terms, expiresAt, oneTime, guardianApproval },
126
129
  domain
127
130
  );
128
131
  }
@@ -137,7 +140,11 @@ export const consentToContract = async (
137
140
 
138
141
  const termsId = uuid();
139
142
 
140
- const result = await new QueryBuilder(new BindParam({ params: flattenObject({ terms }) }))
143
+ const result = await new QueryBuilder(
144
+ new BindParam({
145
+ params: flattenObject({ terms, ...(guardianApproval ? { guardianApproval } : {}) }),
146
+ })
147
+ )
141
148
  .match({
142
149
  multiple: [
143
150
  {
@@ -157,8 +164,8 @@ export const consentToContract = async (
157
164
  properties: {
158
165
  id: termsId,
159
166
  status: oneTime ? 'stale' : 'live',
160
- createdAt: new Date().toISOString(),
161
- updatedAt: new Date().toISOString(),
167
+ createdAt: transaction.date,
168
+ updatedAt: transaction.date,
162
169
  ...(expiresAt ? { expiresAt } : {}),
163
170
  ...(oneTime ? { oneTime } : {}),
164
171
  },
@@ -259,7 +266,10 @@ export const consentToContract = async (
259
266
  boostCredential,
260
267
  contractOwnerSigningAuthority,
261
268
  domain,
262
- false
269
+ true,
270
+ undefined,
271
+ true,
272
+ [getDidWeb(domain, contractOwner.profileId)]
263
273
  );
264
274
 
265
275
  const boostTransaction = {