@learncard/network-brain-service 3.19.0 → 4.0.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 (507) hide show
  1. package/.env.example +47 -82
  2. package/AGENTS.md +52 -0
  3. package/CHANGELOG.md +147 -0
  4. package/README.md +51 -2
  5. package/bootstrapLambda.test.ts +72 -0
  6. package/config/config.dev.json +29 -0
  7. package/config/config.example.json +29 -0
  8. package/config/config.json +1 -0
  9. package/config/config.production.json +29 -0
  10. package/config/config.scouts.dev.json +24 -0
  11. package/config/config.scouts.production.json +24 -0
  12. package/contractEventsLambda.ts +18 -0
  13. package/contractEventsLambdaApp.ts +19 -0
  14. package/didWebLambda.ts +13 -18
  15. package/didWebLambdaApp.ts +22 -0
  16. package/dist/accesslayer/app-manifest-version/create.d.ts +10 -0
  17. package/dist/accesslayer/app-manifest-version/create.d.ts.map +1 -0
  18. package/dist/accesslayer/app-manifest-version/read.d.ts +11 -0
  19. package/dist/accesslayer/app-manifest-version/read.d.ts.map +1 -0
  20. package/dist/accesslayer/app-manifest-version/update.d.ts +7 -0
  21. package/dist/accesslayer/app-manifest-version/update.d.ts.map +1 -0
  22. package/dist/accesslayer/app-store-listing/read.d.ts +2 -1
  23. package/dist/accesslayer/app-store-listing/read.d.ts.map +1 -1
  24. package/dist/accesslayer/app-store-listing/relationships/create.d.ts +1 -0
  25. package/dist/accesslayer/app-store-listing/relationships/create.d.ts.map +1 -1
  26. package/dist/accesslayer/app-store-listing/relationships/read.d.ts +2 -0
  27. package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
  28. package/dist/accesslayer/app-store-listing/update.d.ts +1 -0
  29. package/dist/accesslayer/app-store-listing/update.d.ts.map +1 -1
  30. package/dist/accesslayer/boost/relationships/create.d.ts +2 -1
  31. package/dist/accesslayer/boost/relationships/create.d.ts.map +1 -1
  32. package/dist/accesslayer/consentflowcontract/create.d.ts +9 -1
  33. package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
  34. package/dist/accesslayer/consentflowcontract/read.d.ts +34 -3
  35. package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
  36. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +4 -1
  37. package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
  38. package/dist/accesslayer/consentflowcontract/relationships/delete.d.ts.map +1 -1
  39. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +3 -2
  40. package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
  41. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts +12 -0
  42. package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts.map +1 -0
  43. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts +12 -0
  44. package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts.map +1 -0
  45. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +8 -5
  46. package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
  47. package/dist/accesslayer/credential/create.d.ts +2 -1
  48. package/dist/accesslayer/credential/create.d.ts.map +1 -1
  49. package/dist/accesslayer/credential/relationships/create.d.ts +4 -3
  50. package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
  51. package/dist/accesslayer/inbox-credential/relationships/create.d.ts +2 -1
  52. package/dist/accesslayer/inbox-credential/relationships/create.d.ts.map +1 -1
  53. package/dist/accesslayer/inbox-credential/update.d.ts +2 -1
  54. package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
  55. package/dist/accesslayer/presentation/read.d.ts.map +1 -1
  56. package/dist/accesslayer/presentation/relationships/create.d.ts +2 -2
  57. package/dist/accesslayer/presentation/relationships/create.d.ts.map +1 -1
  58. package/dist/accesslayer/presentation/relationships/read.d.ts.map +1 -1
  59. package/dist/accesslayer/share-link/abandon.d.ts +3 -0
  60. package/dist/accesslayer/share-link/abandon.d.ts.map +1 -0
  61. package/dist/accesslayer/share-link/attachment.d.ts +37 -0
  62. package/dist/accesslayer/share-link/attachment.d.ts.map +1 -0
  63. package/dist/accesslayer/share-link/cleanup.d.ts +6 -0
  64. package/dist/accesslayer/share-link/cleanup.d.ts.map +1 -0
  65. package/dist/accesslayer/share-link/errors.d.ts +8 -0
  66. package/dist/accesslayer/share-link/errors.d.ts.map +1 -0
  67. package/dist/accesslayer/share-link/finalize.d.ts +3 -0
  68. package/dist/accesslayer/share-link/finalize.d.ts.map +1 -0
  69. package/dist/accesslayer/share-link/helpers.d.ts +59 -0
  70. package/dist/accesslayer/share-link/helpers.d.ts.map +1 -0
  71. package/dist/accesslayer/share-link/index.d.ts +12 -0
  72. package/dist/accesslayer/share-link/index.d.ts.map +1 -0
  73. package/dist/accesslayer/share-link/list.d.ts +5 -0
  74. package/dist/accesslayer/share-link/list.d.ts.map +1 -0
  75. package/dist/accesslayer/share-link/read.d.ts +5 -0
  76. package/dist/accesslayer/share-link/read.d.ts.map +1 -0
  77. package/dist/accesslayer/share-link/receipt.d.ts +11 -0
  78. package/dist/accesslayer/share-link/receipt.d.ts.map +1 -0
  79. package/dist/accesslayer/share-link/recovery.d.ts +6 -0
  80. package/dist/accesslayer/share-link/recovery.d.ts.map +1 -0
  81. package/dist/accesslayer/share-link/reserve.d.ts +4 -0
  82. package/dist/accesslayer/share-link/reserve.d.ts.map +1 -0
  83. package/dist/accesslayer/share-link/revoke.d.ts +3 -0
  84. package/dist/accesslayer/share-link/revoke.d.ts.map +1 -0
  85. package/dist/accesslayer/share-link/transaction-execution.d.ts +18 -0
  86. package/dist/accesslayer/share-link/transaction-execution.d.ts.map +1 -0
  87. package/dist/accesslayer/share-link/transaction.d.ts +15 -0
  88. package/dist/accesslayer/share-link/transaction.d.ts.map +1 -0
  89. package/dist/accesslayer/share-link/types.d.ts +375 -0
  90. package/dist/accesslayer/share-link/types.d.ts.map +1 -0
  91. package/dist/app.d.ts +4 -0
  92. package/dist/app.d.ts.map +1 -1
  93. package/dist/cache/app-store.caches.d.ts +49 -0
  94. package/dist/cache/app-store.caches.d.ts.map +1 -1
  95. package/dist/config/applyDockerStageConfig.d.ts +2 -0
  96. package/dist/config/applyDockerStageConfig.d.ts.map +1 -0
  97. package/dist/config/environment.d.ts +14 -0
  98. package/dist/config/environment.d.ts.map +1 -1
  99. package/dist/config/serverlessFunctionEnv.test.d.ts +2 -0
  100. package/dist/config/serverlessFunctionEnv.test.d.ts.map +1 -0
  101. package/dist/config/stageConfig.d.ts +112 -0
  102. package/dist/config/stageConfig.d.ts.map +1 -0
  103. package/dist/config/stageConfig.test.d.ts +2 -0
  104. package/dist/config/stageConfig.test.d.ts.map +1 -0
  105. package/dist/constants/auth-grant.d.ts +2 -0
  106. package/dist/constants/auth-grant.d.ts.map +1 -1
  107. package/dist/didkit_wasm_bg.wasm +0 -0
  108. package/dist/docker-entry.d.ts +1 -1
  109. package/dist/docker-entry.d.ts.map +1 -1
  110. package/dist/helpers/app-manifest.helpers.d.ts +5 -0
  111. package/dist/helpers/app-manifest.helpers.d.ts.map +1 -0
  112. package/dist/helpers/boost.helpers.d.ts.map +1 -1
  113. package/dist/helpers/claim-hooks.helpers.d.ts +2 -1
  114. package/dist/helpers/claim-hooks.helpers.d.ts.map +1 -1
  115. package/dist/helpers/consent-referral.helpers.d.ts +2 -0
  116. package/dist/helpers/consent-referral.helpers.d.ts.map +1 -0
  117. package/dist/helpers/consent-scopes.helpers.d.ts +7 -0
  118. package/dist/helpers/consent-scopes.helpers.d.ts.map +1 -0
  119. package/dist/helpers/contract-event-maintenance.helpers.d.ts +3 -0
  120. package/dist/helpers/contract-event-maintenance.helpers.d.ts.map +1 -0
  121. package/dist/helpers/contract-events.helpers.d.ts +25 -0
  122. package/dist/helpers/contract-events.helpers.d.ts.map +1 -0
  123. package/dist/helpers/contract-requests.helpers.d.ts +19 -0
  124. package/dist/helpers/contract-requests.helpers.d.ts.map +1 -0
  125. package/dist/helpers/credentialSubject.helpers.d.ts +3 -0
  126. package/dist/helpers/credentialSubject.helpers.d.ts.map +1 -0
  127. package/dist/helpers/credentialSubject.helpers.test.d.ts +2 -0
  128. package/dist/helpers/credentialSubject.helpers.test.d.ts.map +1 -0
  129. package/dist/helpers/finalize-inbox.helpers.d.ts +3 -3
  130. package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
  131. package/dist/helpers/inbox.helpers.d.ts.map +1 -1
  132. package/dist/helpers/inline-template.helpers.d.ts +45 -0
  133. package/dist/helpers/inline-template.helpers.d.ts.map +1 -0
  134. package/dist/helpers/notificationMessages.d.ts +1 -1
  135. package/dist/helpers/notificationMessages.d.ts.map +1 -1
  136. package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
  137. package/dist/helpers/notifications.helpers.d.ts +6 -0
  138. package/dist/helpers/notifications.helpers.d.ts.map +1 -1
  139. package/dist/helpers/presentation-metadata.d.ts +4 -0
  140. package/dist/helpers/presentation-metadata.d.ts.map +1 -0
  141. package/dist/helpers/presentation.helpers.d.ts +1 -1
  142. package/dist/helpers/presentation.helpers.d.ts.map +1 -1
  143. package/dist/helpers/profile.helpers.d.ts +2 -0
  144. package/dist/helpers/profile.helpers.d.ts.map +1 -1
  145. package/dist/helpers/share-content-client/adapters.d.ts +10 -0
  146. package/dist/helpers/share-content-client/adapters.d.ts.map +1 -0
  147. package/dist/helpers/share-content-client/canonical.d.ts +7 -0
  148. package/dist/helpers/share-content-client/canonical.d.ts.map +1 -0
  149. package/dist/helpers/share-content-client/client.d.ts +5 -0
  150. package/dist/helpers/share-content-client/client.d.ts.map +1 -0
  151. package/dist/helpers/share-content-client/config.d.ts +11 -0
  152. package/dist/helpers/share-content-client/config.d.ts.map +1 -0
  153. package/dist/helpers/share-content-client/index.d.ts +6 -0
  154. package/dist/helpers/share-content-client/index.d.ts.map +1 -0
  155. package/dist/helpers/share-content-client/responses.d.ts +16 -0
  156. package/dist/helpers/share-content-client/responses.d.ts.map +1 -0
  157. package/dist/helpers/share-content-client/types.d.ts +145 -0
  158. package/dist/helpers/share-content-client/types.d.ts.map +1 -0
  159. package/dist/helpers/share-link-attachment-access.d.ts +4 -0
  160. package/dist/helpers/share-link-attachment-access.d.ts.map +1 -0
  161. package/dist/helpers/share-link-attachment.d.ts +18 -0
  162. package/dist/helpers/share-link-attachment.d.ts.map +1 -0
  163. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts +5 -0
  164. package/dist/helpers/share-link-coordinator/attachment-stat.d.ts.map +1 -0
  165. package/dist/helpers/share-link-coordinator/budget-helpers.d.ts +5 -0
  166. package/dist/helpers/share-link-coordinator/budget-helpers.d.ts.map +1 -0
  167. package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts +3 -0
  168. package/dist/helpers/share-link-coordinator/cleanup-runner.d.ts.map +1 -0
  169. package/dist/helpers/share-link-coordinator/coordinator.d.ts +4 -0
  170. package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -0
  171. package/dist/helpers/share-link-coordinator/index.d.ts +5 -0
  172. package/dist/helpers/share-link-coordinator/index.d.ts.map +1 -0
  173. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts +9 -0
  174. package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -0
  175. package/dist/helpers/share-link-coordinator/runtime.d.ts +11 -0
  176. package/dist/helpers/share-link-coordinator/runtime.d.ts.map +1 -0
  177. package/dist/helpers/share-link-coordinator/stat.d.ts +4 -0
  178. package/dist/helpers/share-link-coordinator/stat.d.ts.map +1 -0
  179. package/dist/helpers/share-link-coordinator/types.d.ts +160 -0
  180. package/dist/helpers/share-link-coordinator/types.d.ts.map +1 -0
  181. package/dist/helpers/share-link-lifecycle/index.d.ts +5 -0
  182. package/dist/helpers/share-link-lifecycle/index.d.ts.map +1 -0
  183. package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts +4 -0
  184. package/dist/helpers/share-link-lifecycle/lease.helpers.d.ts.map +1 -0
  185. package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts +21 -0
  186. package/dist/helpers/share-link-lifecycle/recovery.helpers.d.ts.map +1 -0
  187. package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts +9 -0
  188. package/dist/helpers/share-link-lifecycle/request-hash.helpers.d.ts.map +1 -0
  189. package/dist/helpers/share-link-lifecycle/types.d.ts +14 -0
  190. package/dist/helpers/share-link-lifecycle/types.d.ts.map +1 -0
  191. package/dist/helpers/share-link-maintenance/budget.d.ts +12 -0
  192. package/dist/helpers/share-link-maintenance/budget.d.ts.map +1 -0
  193. package/dist/helpers/share-link-maintenance/config.d.ts +48 -0
  194. package/dist/helpers/share-link-maintenance/config.d.ts.map +1 -0
  195. package/dist/helpers/share-link-maintenance/index.d.ts +8 -0
  196. package/dist/helpers/share-link-maintenance/index.d.ts.map +1 -0
  197. package/dist/helpers/share-link-maintenance/runner.d.ts +20 -0
  198. package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -0
  199. package/dist/helpers/share-link-maintenance/runtime.d.ts +38 -0
  200. package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -0
  201. package/dist/helpers/share-link-maintenance/scheduler.d.ts +18 -0
  202. package/dist/helpers/share-link-maintenance/scheduler.d.ts.map +1 -0
  203. package/dist/helpers/share-link-maintenance/telemetry.d.ts +5 -0
  204. package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -0
  205. package/dist/helpers/share-link-maintenance/types.d.ts +56 -0
  206. package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -0
  207. package/dist/helpers/share-link-owner/config.d.ts +16 -0
  208. package/dist/helpers/share-link-owner/config.d.ts.map +1 -0
  209. package/dist/helpers/share-link-owner/diagnostics.d.ts +26 -0
  210. package/dist/helpers/share-link-owner/diagnostics.d.ts.map +1 -0
  211. package/dist/helpers/share-link-owner/lazy-initializer.d.ts +2 -0
  212. package/dist/helpers/share-link-owner/lazy-initializer.d.ts.map +1 -0
  213. package/dist/helpers/share-link-owner-projection.d.ts +4 -0
  214. package/dist/helpers/share-link-owner-projection.d.ts.map +1 -0
  215. package/dist/helpers/share-link-passcode-abuse.d.ts +2 -0
  216. package/dist/helpers/share-link-passcode-abuse.d.ts.map +1 -0
  217. package/dist/helpers/share-link-passcode.d.ts +6 -0
  218. package/dist/helpers/share-link-passcode.d.ts.map +1 -0
  219. package/dist/helpers/share-link-policy/index.d.ts +4 -0
  220. package/dist/helpers/share-link-policy/index.d.ts.map +1 -0
  221. package/dist/helpers/share-link-policy/production.d.ts +11 -0
  222. package/dist/helpers/share-link-policy/production.d.ts.map +1 -0
  223. package/dist/helpers/share-link-policy/resolver.d.ts +6 -0
  224. package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -0
  225. package/dist/helpers/share-link-policy/types.d.ts +22 -0
  226. package/dist/helpers/share-link-policy/types.d.ts.map +1 -0
  227. package/dist/helpers/share-link-public/config.d.ts +13 -0
  228. package/dist/helpers/share-link-public/config.d.ts.map +1 -0
  229. package/dist/helpers/share-link-view-notification.d.ts +2 -0
  230. package/dist/helpers/share-link-view-notification.d.ts.map +1 -0
  231. package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
  232. package/dist/helpers/smartResume.helpers.d.ts +3 -0
  233. package/dist/helpers/smartResume.helpers.d.ts.map +1 -0
  234. package/dist/index.js +2 -2
  235. package/dist/models/AppManifestVersion.d.ts +20 -0
  236. package/dist/models/AppManifestVersion.d.ts.map +1 -0
  237. package/dist/models/AppStoreListing.d.ts +13 -0
  238. package/dist/models/AppStoreListing.d.ts.map +1 -1
  239. package/dist/models/ConsentFlowContract.d.ts +13 -2
  240. package/dist/models/ConsentFlowContract.d.ts.map +1 -1
  241. package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
  242. package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
  243. package/dist/models/Integration.d.ts +2 -0
  244. package/dist/models/Integration.d.ts.map +1 -1
  245. package/dist/models/Presentation.d.ts +2 -0
  246. package/dist/models/Presentation.d.ts.map +1 -1
  247. package/dist/models/Profile.d.ts +2 -0
  248. package/dist/models/Profile.d.ts.map +1 -1
  249. package/dist/models/ShareLink.d.ts +46 -0
  250. package/dist/models/ShareLink.d.ts.map +1 -0
  251. package/dist/models/ShareViewReceipt.d.ts +22 -0
  252. package/dist/models/ShareViewReceipt.d.ts.map +1 -0
  253. package/dist/models/index.d.ts +2 -0
  254. package/dist/models/index.d.ts.map +1 -1
  255. package/dist/models/share-link-constraints.d.ts +3 -0
  256. package/dist/models/share-link-constraints.d.ts.map +1 -0
  257. package/dist/openapi.d.ts +1 -1
  258. package/dist/openapi.d.ts.map +1 -1
  259. package/dist/public-share-link-http.d.ts +4 -0
  260. package/dist/public-share-link-http.d.ts.map +1 -0
  261. package/dist/routes/app-store.d.ts +619 -0
  262. package/dist/routes/app-store.d.ts.map +1 -1
  263. package/dist/routes/boosts.d.ts +2 -2
  264. package/dist/routes/boosts.d.ts.map +1 -1
  265. package/dist/routes/contracts.d.ts +148 -5
  266. package/dist/routes/contracts.d.ts.map +1 -1
  267. package/dist/routes/credentials.d.ts +35 -1
  268. package/dist/routes/credentials.d.ts.map +1 -1
  269. package/dist/routes/inbox.d.ts +52 -2
  270. package/dist/routes/inbox.d.ts.map +1 -1
  271. package/dist/routes/index.d.ts +153 -0
  272. package/dist/routes/index.d.ts.map +1 -1
  273. package/dist/routes/presentations.d.ts +1 -0
  274. package/dist/routes/presentations.d.ts.map +1 -1
  275. package/dist/routes/profile-manager.d.ts.map +1 -1
  276. package/dist/routes/profiles.d.ts.map +1 -1
  277. package/dist/routes/public-share-links.d.ts +285 -0
  278. package/dist/routes/public-share-links.d.ts.map +1 -0
  279. package/dist/routes/share-links.d.ts +1005 -0
  280. package/dist/routes/share-links.d.ts.map +1 -0
  281. package/dist/routes/test.d.ts +5 -0
  282. package/dist/routes/test.d.ts.map +1 -1
  283. package/dist/routes/workflows.d.ts.map +1 -1
  284. package/dist/services/delivery/adapters/postmark.adapter.d.ts.map +1 -1
  285. package/dist/types/app-manifest.d.ts +339 -0
  286. package/dist/types/app-manifest.d.ts.map +1 -0
  287. package/dist/types/app-store-listing.d.ts +34 -0
  288. package/dist/types/app-store-listing.d.ts.map +1 -1
  289. package/dist/types/consentflowcontract.d.ts +31 -4
  290. package/dist/types/consentflowcontract.d.ts.map +1 -1
  291. package/dist/types/credential-refresh.d.ts +1 -1
  292. package/esbuildPlugins.cjs +1 -0
  293. package/lambda.ts +49 -192
  294. package/lambdaApp.ts +203 -0
  295. package/package.json +25 -17
  296. package/scripts/export-openapi.ts +20 -0
  297. package/scripts/offline-schema-environment.ts +18 -0
  298. package/scripts/seed-demo-persona.ts +425 -0
  299. package/scripts/verify-python-contracts.ts +72 -0
  300. package/serverless-local.yml +3 -6
  301. package/serverless.function-env.cjs +50 -0
  302. package/serverless.local.yml +0 -1
  303. package/serverless.yml +44 -55
  304. package/shareLinkMaintenanceLambda.ts +18 -0
  305. package/shareLinkMaintenanceLambdaApp.ts +50 -0
  306. package/src/accesslayer/app-manifest-version/create.ts +57 -0
  307. package/src/accesslayer/app-manifest-version/read.ts +129 -0
  308. package/src/accesslayer/app-manifest-version/update.ts +61 -0
  309. package/src/accesslayer/app-store-listing/read.ts +6 -0
  310. package/src/accesslayer/app-store-listing/relationships/create.ts +20 -0
  311. package/src/accesslayer/app-store-listing/relationships/read.ts +24 -1
  312. package/src/accesslayer/app-store-listing/update.ts +15 -0
  313. package/src/accesslayer/boost/relationships/create.ts +8 -2
  314. package/src/accesslayer/consentflowcontract/create.ts +81 -8
  315. package/src/accesslayer/consentflowcontract/read.ts +84 -10
  316. package/src/accesslayer/consentflowcontract/relationships/create.ts +125 -109
  317. package/src/accesslayer/consentflowcontract/relationships/delete.ts +1 -0
  318. package/src/accesslayer/consentflowcontract/relationships/read.ts +184 -108
  319. package/src/accesslayer/consentflowcontract/relationships/recipients.ts +125 -0
  320. package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +125 -0
  321. package/src/accesslayer/consentflowcontract/relationships/update.ts +255 -217
  322. package/src/accesslayer/credential/create.test.ts +2 -115
  323. package/src/accesslayer/credential/create.ts +11 -6
  324. package/src/accesslayer/credential/relationships/create.ts +17 -10
  325. package/src/accesslayer/inbox-credential/relationships/create.ts +70 -48
  326. package/src/accesslayer/inbox-credential/update.ts +4 -2
  327. package/src/accesslayer/presentation/read.ts +57 -25
  328. package/src/accesslayer/presentation/relationships/create.ts +54 -11
  329. package/src/accesslayer/presentation/relationships/read.ts +21 -6
  330. package/src/accesslayer/share-link/abandon.ts +120 -0
  331. package/src/accesslayer/share-link/attachment.ts +360 -0
  332. package/src/accesslayer/share-link/cleanup.ts +271 -0
  333. package/src/accesslayer/share-link/errors.ts +32 -0
  334. package/src/accesslayer/share-link/finalize.ts +313 -0
  335. package/src/accesslayer/share-link/helpers.ts +450 -0
  336. package/src/accesslayer/share-link/index.ts +11 -0
  337. package/src/accesslayer/share-link/list.ts +162 -0
  338. package/src/accesslayer/share-link/read.ts +107 -0
  339. package/src/accesslayer/share-link/receipt.ts +548 -0
  340. package/src/accesslayer/share-link/recovery.ts +565 -0
  341. package/src/accesslayer/share-link/reserve.ts +763 -0
  342. package/src/accesslayer/share-link/revoke.ts +232 -0
  343. package/src/accesslayer/share-link/transaction-execution.ts +72 -0
  344. package/src/accesslayer/share-link/transaction.ts +126 -0
  345. package/src/accesslayer/share-link/types.ts +606 -0
  346. package/src/app.ts +6 -0
  347. package/src/cache/app-store.caches.ts +35 -0
  348. package/src/config/applyDockerStageConfig.ts +6 -0
  349. package/src/config/environment.test.ts +17 -12
  350. package/src/config/environment.ts +15 -0
  351. package/src/config/serverlessFunctionEnv.test.ts +105 -0
  352. package/src/config/stageConfig.test.ts +25 -0
  353. package/src/config/stageConfig.ts +24 -0
  354. package/src/constants/auth-grant.ts +5 -0
  355. package/src/docker-entry.ts +81 -0
  356. package/src/helpers/app-manifest.helpers.ts +185 -0
  357. package/src/helpers/boost.helpers.ts +4 -31
  358. package/src/helpers/claim-hooks.helpers.ts +20 -7
  359. package/src/helpers/consent-referral.helpers.ts +6 -0
  360. package/src/helpers/consent-scopes.helpers.ts +58 -0
  361. package/src/helpers/contract-event-maintenance.helpers.ts +43 -0
  362. package/src/helpers/contract-events.helpers.ts +540 -0
  363. package/src/helpers/contract-requests.helpers.ts +211 -0
  364. package/src/helpers/credentialSubject.helpers.test.ts +114 -0
  365. package/src/helpers/credentialSubject.helpers.ts +40 -0
  366. package/src/helpers/finalize-inbox.helpers.ts +163 -44
  367. package/src/helpers/inbox.helpers.ts +58 -70
  368. package/src/helpers/inline-template.helpers.ts +560 -0
  369. package/src/helpers/notificationMessages.ts +77 -1
  370. package/src/helpers/notificationQueue.helpers.ts +23 -5
  371. package/src/helpers/notifications.helpers.ts +25 -1
  372. package/src/helpers/presentation-metadata.ts +41 -0
  373. package/src/helpers/presentation.helpers.ts +27 -20
  374. package/src/helpers/profile.helpers.ts +13 -0
  375. package/src/helpers/share-content-client/adapters.ts +55 -0
  376. package/src/helpers/share-content-client/canonical.ts +25 -0
  377. package/src/helpers/share-content-client/client.ts +440 -0
  378. package/src/helpers/share-content-client/config.ts +189 -0
  379. package/src/helpers/share-content-client/index.ts +27 -0
  380. package/src/helpers/share-content-client/responses.ts +273 -0
  381. package/src/helpers/share-content-client/types.ts +253 -0
  382. package/src/helpers/share-link-attachment-access.ts +87 -0
  383. package/src/helpers/share-link-attachment.ts +115 -0
  384. package/src/helpers/share-link-coordinator/attachment-stat.ts +41 -0
  385. package/src/helpers/share-link-coordinator/budget-helpers.ts +53 -0
  386. package/src/helpers/share-link-coordinator/cleanup-runner.ts +286 -0
  387. package/src/helpers/share-link-coordinator/coordinator.ts +727 -0
  388. package/src/helpers/share-link-coordinator/index.ts +8 -0
  389. package/src/helpers/share-link-coordinator/recovery-runner.ts +443 -0
  390. package/src/helpers/share-link-coordinator/runtime.ts +88 -0
  391. package/src/helpers/share-link-coordinator/stat.ts +30 -0
  392. package/src/helpers/share-link-coordinator/types.ts +353 -0
  393. package/src/helpers/share-link-lifecycle/index.ts +4 -0
  394. package/src/helpers/share-link-lifecycle/lease.helpers.ts +35 -0
  395. package/src/helpers/share-link-lifecycle/recovery.helpers.ts +173 -0
  396. package/src/helpers/share-link-lifecycle/request-hash.helpers.ts +130 -0
  397. package/src/helpers/share-link-lifecycle/types.ts +58 -0
  398. package/src/helpers/share-link-maintenance/budget.ts +63 -0
  399. package/src/helpers/share-link-maintenance/config.ts +312 -0
  400. package/src/helpers/share-link-maintenance/index.ts +24 -0
  401. package/src/helpers/share-link-maintenance/runner.ts +251 -0
  402. package/src/helpers/share-link-maintenance/runtime.ts +340 -0
  403. package/src/helpers/share-link-maintenance/scheduler.ts +126 -0
  404. package/src/helpers/share-link-maintenance/telemetry.ts +73 -0
  405. package/src/helpers/share-link-maintenance/types.ts +91 -0
  406. package/src/helpers/share-link-owner/config.ts +48 -0
  407. package/src/helpers/share-link-owner/diagnostics.ts +89 -0
  408. package/src/helpers/share-link-owner/lazy-initializer.ts +29 -0
  409. package/src/helpers/share-link-owner-projection.ts +54 -0
  410. package/src/helpers/share-link-passcode-abuse.ts +36 -0
  411. package/src/helpers/share-link-passcode.ts +47 -0
  412. package/src/helpers/share-link-policy/index.ts +7 -0
  413. package/src/helpers/share-link-policy/production.ts +108 -0
  414. package/src/helpers/share-link-policy/resolver.ts +113 -0
  415. package/src/helpers/share-link-policy/types.ts +56 -0
  416. package/src/helpers/share-link-public/config.ts +41 -0
  417. package/src/helpers/share-link-view-notification.ts +18 -0
  418. package/src/helpers/signingAuthority.helpers.ts +23 -14
  419. package/src/helpers/smartResume.helpers.ts +123 -0
  420. package/src/models/AppManifestVersion.ts +43 -0
  421. package/src/models/AppStoreListing.ts +32 -0
  422. package/src/models/ConsentFlowContract.ts +34 -3
  423. package/src/models/ConsentFlowTerms.ts +10 -0
  424. package/src/models/ConsentFlowTransaction.ts +3 -0
  425. package/src/models/Integration.ts +2 -0
  426. package/src/models/Presentation.ts +2 -2
  427. package/src/models/Profile.ts +3 -2
  428. package/src/models/ShareLink.ts +134 -0
  429. package/src/models/ShareViewReceipt.ts +69 -0
  430. package/src/models/index.ts +25 -0
  431. package/src/models/share-link-constraints.ts +98 -0
  432. package/src/openapi.ts +44 -8
  433. package/src/public-share-link-http.ts +49 -0
  434. package/src/routes/app-store.ts +1056 -38
  435. package/src/routes/boosts.ts +14 -25
  436. package/src/routes/contracts.ts +384 -273
  437. package/src/routes/inbox.ts +4 -2
  438. package/src/routes/index.ts +142 -44
  439. package/src/routes/presentations.ts +19 -3
  440. package/src/routes/profile-manager.ts +29 -2
  441. package/src/routes/profiles.ts +29 -6
  442. package/src/routes/public-share-links.ts +983 -0
  443. package/src/routes/share-links.ts +709 -0
  444. package/src/routes/workflows.ts +3 -2
  445. package/src/services/delivery/adapters/postmark.adapter.ts +4 -1
  446. package/src/types/app-manifest.ts +28 -0
  447. package/src/types/app-store-listing.ts +13 -0
  448. package/src/types/consentflowcontract.ts +25 -4
  449. package/test/app-manifest.helpers.spec.ts +170 -0
  450. package/test/app-manifest.routes.spec.ts +299 -0
  451. package/test/app-store-listing-updates.spec.ts +236 -0
  452. package/test/app-store-listing.spec.ts +65 -3
  453. package/test/consent-data-boundaries.spec.ts +352 -0
  454. package/test/consent-recipients.spec.ts +513 -0
  455. package/test/consent-scopes.helpers.spec.ts +67 -0
  456. package/test/consentflow.spec.ts +63 -2
  457. package/test/contract-requests.spec.ts +1588 -0
  458. package/test/credential-storage.neo4j.spec.ts +122 -0
  459. package/test/dids.spec.ts +10 -1
  460. package/test/guardian-gated-routes.spec.ts +49 -0
  461. package/test/helpers/share-link-fixtures.ts +94 -0
  462. package/test/inbox-security.e2e.ts +26 -10
  463. package/test/inbox.spec.ts +184 -8
  464. package/test/inline-template.helpers.spec.ts +49 -0
  465. package/test/managed-recipient-inbox.spec.ts +152 -0
  466. package/test/notificationMessages.spec.ts +13 -0
  467. package/test/openapi.spec.ts +14 -53
  468. package/test/presentations.spec.ts +54 -6
  469. package/test/profiles.spec.ts +97 -1
  470. package/test/share-content-client.contract.spec.ts +365 -0
  471. package/test/share-content-client.unit.spec.ts +462 -0
  472. package/test/share-link-abuse.unit.spec.ts +65 -0
  473. package/test/share-link-attachment.neo4j.spec.ts +262 -0
  474. package/test/share-link-attachment.unit.spec.ts +244 -0
  475. package/test/share-link-coordinator.neo4j.spec.ts +684 -0
  476. package/test/share-link-coordinator.unit.spec.ts +990 -0
  477. package/test/share-link-dependency-diagnostics.unit.spec.ts +127 -0
  478. package/test/share-link-lifecycle.neo4j.spec.ts +1411 -0
  479. package/test/share-link-lifecycle.unit.spec.ts +205 -0
  480. package/test/share-link-list-cursor.unit.spec.ts +86 -0
  481. package/test/share-link-list.neo4j.spec.ts +324 -0
  482. package/test/share-link-maintenance.neo4j.spec.ts +355 -0
  483. package/test/share-link-maintenance.unit.spec.ts +1140 -0
  484. package/test/share-link-owner-policy.unit.spec.ts +210 -0
  485. package/test/share-link-owner-route.unit.spec.ts +820 -0
  486. package/test/share-link-owner-runtime.unit.spec.ts +102 -0
  487. package/test/share-link-passcode.unit.spec.ts +29 -0
  488. package/test/share-link-policy.unit.spec.ts +149 -0
  489. package/test/share-link-presentation-metadata.unit.spec.ts +49 -0
  490. package/test/share-link-production-policy.unit.spec.ts +126 -0
  491. package/test/share-link-public-config.unit.spec.ts +64 -0
  492. package/test/share-link-public-http-adapter.unit.spec.ts +315 -0
  493. package/test/share-link-public-route.unit.spec.ts +1079 -0
  494. package/test/share-link-receipt.neo4j.spec.ts +943 -0
  495. package/test/share-link-recovery.neo4j.spec.ts +935 -0
  496. package/test/share-link-recovery.unit.spec.ts +726 -0
  497. package/test/share-link-service-policy.neo4j.spec.ts +403 -0
  498. package/test/signing-authority-encryption.spec.ts +75 -0
  499. package/test/smart-resume-consent.spec.ts +233 -0
  500. package/test/smart-resume-upload.spec.ts +65 -0
  501. package/tsconfig.build.json +1 -1
  502. package/tsconfig.json +1 -0
  503. package/vitest.config.ts +14 -0
  504. package/vitest.inbox-e2e.config.ts +2 -0
  505. package/vitest.share-links-neo4j.config.ts +19 -0
  506. package/vitest.share-links-unit.config.ts +28 -0
  507. package/workerLambda.test.ts +100 -0
@@ -0,0 +1,425 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Publish a credential-library persona as a ConsentFlow contract with auto-boosts.
4
+ *
5
+ * Usage (from services/learn-card-network/brain-service):
6
+ * bun run seed:demo-persona student
7
+ *
8
+ * The script is idempotent: profile, signing authority, Boost, contract, and
9
+ * AUTO_RECEIVE identifiers are stable. Re-running updates fixture content and
10
+ * replaces the contract's auto-boost set without changing its URI.
11
+ *
12
+ * Every target requires explicit database, domain, signing-authority endpoint,
13
+ * and 64-character signing seed configuration.
14
+ */
15
+
16
+ import { fileURLToPath } from 'url';
17
+ import { createHmac } from 'node:crypto';
18
+
19
+ import * as bs58 from 'bs58';
20
+ import * as dotenv from 'dotenv';
21
+ import { MongoClient } from 'mongodb';
22
+ import { Neogma } from 'neogma';
23
+ import Redis from 'ioredis';
24
+ import * as nacl from 'tweetnacl';
25
+ import { v4 as uuid, v5 as uuidv5 } from 'uuid';
26
+
27
+ import {
28
+ getBundle,
29
+ getFixture,
30
+ prepareFixture,
31
+ type CredentialBundleEntry,
32
+ type CredentialBundleIssuer,
33
+ } from '@learncard/credential-library';
34
+ import type { UnsignedVC } from '@learncard/types';
35
+ import { flattenObject } from '../src/helpers/objects.helpers';
36
+
37
+ dotenv.config({ path: fileURLToPath(new URL('../.env', import.meta.url)) });
38
+
39
+ const PERSONA_NAMESPACE = '5c4bb193-6e65-43d9-940d-d85b758a94f2';
40
+ const SIGNING_AUTHORITY_NAME = 'sample-personas';
41
+
42
+ const NEO4J_URI = process.env.NEO4J_URI ?? 'bolt://localhost:7687';
43
+ const NEO4J_USERNAME = process.env.NEO4J_USERNAME ?? 'neo4j';
44
+ const NEO4J_PASSWORD = process.env.NEO4J_PASSWORD ?? 'this-is-the-password';
45
+ const MONGO_URI = process.env.MONGO_URI ?? 'mongodb://localhost:27017/?replicaSet=rs0';
46
+ const MONGO_DB_NAME = process.env.MONGO_DB_NAME ?? 'lca-api';
47
+ const REDIS_HOST = process.env.REDIS_HOST ?? 'localhost';
48
+ const REDIS_PORT = Number.parseInt(process.env.REDIS_PORT ?? '6379', 10);
49
+ const BRAIN_DOMAIN = process.env.DOMAIN_NAME || 'localhost%3A4000';
50
+ const SIGNING_AUTHORITY_ENDPOINT = process.env.DEMO_PERSONA_SIGNING_AUTHORITY_ENDPOINT;
51
+ const SIGNING_AUTHORITY_SEED = process.env.DEMO_PERSONA_SA_SEED;
52
+
53
+ const personaId = process.argv[2];
54
+
55
+ if (!personaId) {
56
+ throw new Error('Usage: bun scripts/seed-demo-persona.ts <personaId>');
57
+ }
58
+
59
+ if (!SIGNING_AUTHORITY_SEED || !SIGNING_AUTHORITY_ENDPOINT) {
60
+ throw new Error(
61
+ 'DEMO_PERSONA_SA_SEED and DEMO_PERSONA_SIGNING_AUTHORITY_ENDPOINT are required.'
62
+ );
63
+ }
64
+
65
+ if (!/^[0-9a-f]{64}$/i.test(SIGNING_AUTHORITY_SEED)) {
66
+ throw new Error('DEMO_PERSONA_SA_SEED must contain exactly 64 hexadecimal characters.');
67
+ }
68
+
69
+ const transformProfileId = (raw: string): string => raw.toLowerCase().replace(/:/g, '%3A');
70
+
71
+ const getDidWeb = (domain: string, profileId: string): string =>
72
+ `did:web:${domain}:users:${profileId}`;
73
+
74
+ const constructContractUri = (contractId: string): string => {
75
+ const encodedDomain = BRAIN_DOMAIN.includes('localhost')
76
+ ? BRAIN_DOMAIN.replace(/:/g, '%3A')
77
+ : BRAIN_DOMAIN.replace(/:/g, '/');
78
+
79
+ return `lc:network:${encodedDomain.replace(/\/trpc$/, '')}/trpc:contract:${contractId}`;
80
+ };
81
+
82
+ const deriveDidKeyFromSeed = (hexSeed: string): string => {
83
+ const seedBytes = Buffer.from(hexSeed, 'hex');
84
+ const keyPair = nacl.sign.keyPair.fromSeed(seedBytes);
85
+ const multicodec = Buffer.concat([Buffer.from('ed01', 'hex'), Buffer.from(keyPair.publicKey)]);
86
+
87
+ return `did:key:z${bs58.encode(multicodec)}`;
88
+ };
89
+ const deriveSigningAuthoritySeed = (profileId: string): string =>
90
+ createHmac('sha256', Buffer.from(SIGNING_AUTHORITY_SEED, 'hex'))
91
+ .update(`learncard:sample-persona:${profileId}`)
92
+ .digest('hex');
93
+
94
+ const clearProfileDidDocumentCache = async (profileId: string): Promise<void> => {
95
+ const redis = new Redis({
96
+ host: REDIS_HOST,
97
+ port: REDIS_PORT,
98
+ lazyConnect: true,
99
+ connectTimeout: 2_000,
100
+ retryStrategy: () => null,
101
+ });
102
+
103
+ try {
104
+ await redis.connect();
105
+ await redis.del(`did-doc:${profileId}`);
106
+ } catch {
107
+ console.warn(
108
+ `Could not clear did-doc:${profileId} at ${REDIS_HOST}:${REDIS_PORT}; delete that Redis key manually, or restart the brain service if it uses an in-memory cache.`
109
+ );
110
+ } finally {
111
+ redis.disconnect();
112
+ }
113
+ };
114
+
115
+ const shiftDate = (offsetDays: number): string => {
116
+ const date = new Date();
117
+ date.setUTCDate(date.getUTCDate() + offsetDays);
118
+ return date.toISOString();
119
+ };
120
+
121
+ const prepareBundleCredential = (entry: CredentialBundleEntry, issuerDid: string): UnsignedVC => {
122
+ const fixture = getFixture(entry.fixtureId);
123
+ const prepared = prepareFixture(fixture, {
124
+ issuerDid,
125
+ subjectDid: 'did:example:123',
126
+ validFrom: shiftDate(entry.validFromOffsetDays),
127
+ freshIds: false,
128
+ });
129
+
130
+ if (entry.name) prepared.name = entry.name;
131
+
132
+ return prepared;
133
+ };
134
+
135
+ const toBoostTemplate = (credential: UnsignedVC, issuerDid: string): string => {
136
+ const template = structuredClone(credential) as Record<string, unknown>;
137
+ delete template.id;
138
+ delete template.proof;
139
+ template.issuer = issuerDid;
140
+
141
+ const subjects = Array.isArray(template.credentialSubject)
142
+ ? template.credentialSubject
143
+ : [template.credentialSubject];
144
+ for (const subject of subjects) {
145
+ if (subject && typeof subject === 'object') {
146
+ Object.assign(subject, { id: 'did:example:123' });
147
+ }
148
+ }
149
+
150
+ return JSON.stringify(template);
151
+ };
152
+
153
+ const ensureSigningAuthority = async (
154
+ ownerDid: string,
155
+ did: string,
156
+ seed: string
157
+ ): Promise<void> => {
158
+ const client = new MongoClient(MONGO_URI);
159
+
160
+ try {
161
+ await client.connect();
162
+ await client
163
+ .db(MONGO_DB_NAME)
164
+ .collection('signingauthorities')
165
+ .updateOne(
166
+ { ownerDid, name: SIGNING_AUTHORITY_NAME },
167
+ {
168
+ $set: { seed, did },
169
+ $setOnInsert: { _id: uuid(), ownerDid, name: SIGNING_AUTHORITY_NAME },
170
+ },
171
+ { upsert: true }
172
+ );
173
+ } finally {
174
+ await client.close();
175
+ }
176
+ };
177
+
178
+ const main = async (): Promise<void> => {
179
+ const bundle = getBundle(personaId);
180
+ const [firstEntry] = bundle.entries;
181
+ if (!firstEntry) {
182
+ throw new Error(`Credential bundle ${bundle.id} has no entries.`);
183
+ }
184
+
185
+ const issuerProfiles = new Map<
186
+ string,
187
+ CredentialBundleIssuer & {
188
+ profileId: `sample-${string}`;
189
+ did: string;
190
+ signingAuthorityDid: string;
191
+ signingAuthoritySeed: string;
192
+ }
193
+ >();
194
+ for (const entry of bundle.entries) {
195
+ const profileId = transformProfileId(entry.issuer.profileId);
196
+ if (!profileId.startsWith('sample-')) {
197
+ throw new Error(`Refusing to seed non-sample issuer profile: ${profileId}`);
198
+ }
199
+ const existingIssuer = issuerProfiles.get(profileId);
200
+
201
+ if (
202
+ existingIssuer &&
203
+ (existingIssuer.displayName !== entry.issuer.displayName ||
204
+ existingIssuer.image !== entry.issuer.image)
205
+ ) {
206
+ throw new Error(`Conflicting metadata configured for sample issuer ${profileId}.`);
207
+ }
208
+
209
+ const signingAuthoritySeed = deriveSigningAuthoritySeed(profileId);
210
+ const signingAuthorityDid = deriveDidKeyFromSeed(signingAuthoritySeed);
211
+ issuerProfiles.set(profileId, {
212
+ ...entry.issuer,
213
+ profileId: profileId as `sample-${string}`,
214
+ did: getDidWeb(BRAIN_DOMAIN, profileId),
215
+ signingAuthorityDid,
216
+ signingAuthoritySeed,
217
+ });
218
+ }
219
+
220
+ const contractOwnerProfileId = transformProfileId(firstEntry.issuer.profileId);
221
+ const contractId = uuidv5(`persona:${bundle.id}:contract`, PERSONA_NAMESPACE);
222
+ const neogma = new Neogma({
223
+ url: NEO4J_URI,
224
+ username: NEO4J_USERNAME,
225
+ password: NEO4J_PASSWORD,
226
+ });
227
+ const run = neogma.queryRunner.run.bind(neogma.queryRunner);
228
+
229
+ try {
230
+ for (const issuer of issuerProfiles.values()) {
231
+ const profileResult = await run(
232
+ `MERGE (p:Profile {profileId: $profileId})
233
+ ON CREATE SET p.isSamplePersona = true
234
+ WITH p
235
+ WHERE p.isSamplePersona = true
236
+ AND (p.did IS NULL OR p.did = $did)
237
+ SET p.displayName = $displayName,
238
+ p.shortBio = $shortBio,
239
+ p.image = $image,
240
+ p.did = $did
241
+ RETURN p.did AS did`,
242
+ {
243
+ profileId: issuer.profileId,
244
+ displayName: issuer.displayName,
245
+ shortBio: 'Issuer for LearnCard sample credentials',
246
+ image: issuer.image ?? '',
247
+ did: issuer.signingAuthorityDid,
248
+ }
249
+ );
250
+ const storedSigningAuthorityDid = profileResult.records[0]?.get('did');
251
+ if (!storedSigningAuthorityDid) {
252
+ throw new Error(
253
+ `Refusing to overwrite existing non-sample profile ${issuer.profileId}.`
254
+ );
255
+ }
256
+ if (storedSigningAuthorityDid !== issuer.signingAuthorityDid) {
257
+ throw new Error(
258
+ `Profile ${issuer.profileId} did not retain its signing authority DID.`
259
+ );
260
+ }
261
+
262
+ await run(
263
+ `MERGE (sa:SigningAuthority {endpoint: $endpoint})
264
+ WITH sa
265
+ MATCH (p:Profile {profileId: $profileId})
266
+ MERGE (p)-[r:USES_SIGNING_AUTHORITY {name: $name}]->(sa)
267
+ SET r.did = $did, r.isPrimary = true
268
+ RETURN r`,
269
+ {
270
+ endpoint: SIGNING_AUTHORITY_ENDPOINT,
271
+ profileId: issuer.profileId,
272
+ name: SIGNING_AUTHORITY_NAME,
273
+ did: issuer.signingAuthorityDid,
274
+ }
275
+ );
276
+ await clearProfileDidDocumentCache(issuer.profileId);
277
+ await ensureSigningAuthority(
278
+ issuer.did,
279
+ issuer.signingAuthorityDid,
280
+ issuer.signingAuthoritySeed
281
+ );
282
+ }
283
+
284
+ const autoBoosts: Array<{ boostId: string; issuerProfileId: string }> = [];
285
+ for (const entry of bundle.entries) {
286
+ const fixture = getFixture(entry.fixtureId);
287
+ const profileId = transformProfileId(entry.issuer.profileId);
288
+ const issuer = issuerProfiles.get(profileId);
289
+ if (!issuer) {
290
+ throw new Error(`Missing seeded issuer profile ${profileId}.`);
291
+ }
292
+
293
+ const boostId = uuidv5(
294
+ `persona:${bundle.id}:fixture:${entry.fixtureId}`,
295
+ PERSONA_NAMESPACE
296
+ );
297
+ const credential = prepareBundleCredential(entry, issuer.did);
298
+ const boost = toBoostTemplate(credential, issuer.did);
299
+ autoBoosts.push({ boostId, issuerProfileId: profileId });
300
+ const boostProperties = flattenObject({
301
+ id: boostId,
302
+ boost,
303
+ name: entry.name ?? fixture.name,
304
+ type: fixture.profile,
305
+ category: 'Achievement',
306
+ status: 'LIVE',
307
+ meta: { personaId: bundle.id, fixtureId: fixture.id },
308
+ });
309
+
310
+ const boostResult = await run(
311
+ `MERGE (b:Boost {id: $boostId})
312
+ SET b = $properties
313
+ WITH b
314
+ OPTIONAL MATCH (b)-[oldCreated:CREATED_BY]->(:Profile)
315
+ DELETE oldCreated
316
+ WITH DISTINCT b
317
+ OPTIONAL MATCH (:Profile)-[oldRole:HAS_ROLE {roleId: '__creator__'}]->(b)
318
+ DELETE oldRole
319
+ WITH DISTINCT b
320
+ MATCH (p:Profile {profileId: $profileId})
321
+ MERGE (b)-[created:CREATED_BY]->(p)
322
+ SET created.date = $date
323
+ MERGE (p)-[:HAS_ROLE {roleId: '__creator__'}]->(b)
324
+ WITH b
325
+ MATCH (b)-[:CREATED_BY]->(creator:Profile)
326
+ MATCH (roleOwner:Profile)-[:HAS_ROLE {roleId: '__creator__'}]->(b)
327
+ RETURN collect(creator.profileId) AS creatorProfileIds,
328
+ collect(roleOwner.profileId) AS roleOwnerProfileIds`,
329
+ {
330
+ boostId,
331
+ properties: boostProperties,
332
+ profileId,
333
+ date: new Date().toISOString(),
334
+ }
335
+ );
336
+ const creatorProfileIds = boostResult.records[0]?.get('creatorProfileIds');
337
+ const roleOwnerProfileIds = boostResult.records[0]?.get('roleOwnerProfileIds');
338
+ if (
339
+ creatorProfileIds?.length !== 1 ||
340
+ creatorProfileIds[0] !== profileId ||
341
+ roleOwnerProfileIds?.length !== 1 ||
342
+ roleOwnerProfileIds[0] !== profileId
343
+ ) {
344
+ throw new Error(`Boost ${boostId} has ambiguous profile ownership.`);
345
+ }
346
+ }
347
+
348
+ const contract = {
349
+ read: {
350
+ personal: { name: { required: false } },
351
+ credentials: { categories: { Achievement: { required: false } } },
352
+ },
353
+ write: {
354
+ personal: {},
355
+ credentials: {
356
+ categories: { Achievement: { required: false, defaultEnabled: true } },
357
+ },
358
+ },
359
+ };
360
+ const now = new Date().toISOString();
361
+ const contractProperties = flattenObject({
362
+ id: contractId,
363
+ name: `${bundle.displayName} Sample Credentials`,
364
+ subtitle: bundle.blurb,
365
+ description: bundle.blurb,
366
+ reasonForAccessing: '',
367
+ needsGuardianConsent: false,
368
+ redirectUrl: '',
369
+ frontDoorBoostUri: '',
370
+ image: '',
371
+ contract,
372
+ updatedAt: now,
373
+ });
374
+
375
+ await run(
376
+ `MERGE (c:ConsentFlowContract {id: $contractId})
377
+ ON CREATE SET c.createdAt = $createdAt
378
+ WITH c, c.createdAt AS createdAt
379
+ SET c = $properties,
380
+ c.createdAt = createdAt
381
+ WITH c
382
+ OPTIONAL MATCH (c)-[oldOwner:CREATED_BY]->(:Profile)
383
+ DELETE oldOwner
384
+ WITH DISTINCT c
385
+ MATCH (p:Profile {profileId: $profileId})
386
+ MERGE (c)-[:CREATED_BY]->(p)
387
+ RETURN c`,
388
+ {
389
+ contractId,
390
+ createdAt: now,
391
+ profileId: contractOwnerProfileId,
392
+ properties: contractProperties,
393
+ }
394
+ );
395
+
396
+ await run(
397
+ `MATCH (c:ConsentFlowContract {id: $contractId})
398
+ OPTIONAL MATCH (c)-[old:AUTO_RECEIVE]->(:Boost)
399
+ DELETE old
400
+ WITH DISTINCT c
401
+ UNWIND $autoBoosts AS autoBoost
402
+ MATCH (b:Boost {id: autoBoost.boostId})
403
+ CREATE (c)-[:AUTO_RECEIVE {
404
+ signingAuthorityEndpoint: $endpoint,
405
+ signingAuthorityName: $name,
406
+ issuer: autoBoost.issuerProfileId
407
+ }]->(b)`,
408
+ {
409
+ contractId,
410
+ autoBoosts,
411
+ endpoint: SIGNING_AUTHORITY_ENDPOINT,
412
+ name: SIGNING_AUTHORITY_NAME,
413
+ }
414
+ );
415
+
416
+ console.log(constructContractUri(contractId));
417
+ } finally {
418
+ await neogma.driver.close();
419
+ }
420
+ };
421
+
422
+ main().catch(error => {
423
+ console.error(error);
424
+ process.exitCode = 1;
425
+ });
@@ -0,0 +1,72 @@
1
+ import assert from 'node:assert/strict';
2
+ import type { ZodType } from 'zod';
3
+
4
+ import { configureOfflineSchemaEnvironment } from './offline-schema-environment';
5
+
6
+ type CapturedRequest = {
7
+ method: string;
8
+ path: string;
9
+ body: unknown;
10
+ serverValid: boolean;
11
+ };
12
+
13
+ const capture = process.argv[2];
14
+ if (!capture)
15
+ throw new Error(
16
+ 'Usage: bun --conditions=development scripts/verify-python-contracts.ts CAPTURE'
17
+ );
18
+ configureOfflineSchemaEnvironment();
19
+
20
+ // Static imports would initialize router dependencies before credentials are removed.
21
+ const { AllocateCredentialRefreshInputValidator, PublishCredentialRefreshInputValidator } =
22
+ await import('@learncard/types');
23
+ const { activityRouter } = await import('../src/routes/activity');
24
+
25
+ const activityInputs: Record<string, ZodType> = {
26
+ '/activity/credentials': activityRouter._def.procedures.getMyActivities._def.inputs.at(
27
+ -1
28
+ ) as ZodType,
29
+ '/activity/credentials/stats': activityRouter._def.procedures.getActivityStats._def.inputs.at(
30
+ -1
31
+ ) as ZodType,
32
+ };
33
+ const records: CapturedRequest[] = await Bun.file(capture).json();
34
+ for (const record of records) {
35
+ const url = new URL(record.path, 'http://offline.invalid');
36
+ let valid: boolean;
37
+ if (url.pathname === '/credential-refresh/publish') {
38
+ valid = PublishCredentialRefreshInputValidator.safeParse(record.body).success;
39
+ } else if (url.pathname === '/credential-refresh/allocate') {
40
+ valid = AllocateCredentialRefreshInputValidator.safeParse(record.body).success;
41
+ } else {
42
+ const validator = activityInputs[url.pathname];
43
+ assert(validator, `No source validator for captured path ${url.pathname}`);
44
+ valid = validator.safeParse(Object.fromEntries(url.searchParams)).success;
45
+ }
46
+ assert.equal(
47
+ valid,
48
+ record.serverValid,
49
+ `Source contract rejected/accepted unexpected input: ${record.method} ${record.path}`
50
+ );
51
+ }
52
+
53
+ // The historical client format and offset suffixes must remain invalid server input.
54
+ for (const validator of Object.values(activityInputs)) {
55
+ for (const date of [
56
+ '2026-01-02T03:04:05.000000+0000',
57
+ '2026-01-02T03:04:05+00:00',
58
+ '2026-01-02T03:04:05',
59
+ ]) {
60
+ for (const field of ['startDate', 'endDate']) {
61
+ assert.equal(
62
+ validator.safeParse({ [field]: date }).success,
63
+ false,
64
+ `Server unexpectedly accepts ${field}=${date}`
65
+ );
66
+ }
67
+ }
68
+ }
69
+ console.log(
70
+ `Verified ${records.length} captured publication/allocation/activity cases and Z-only boundaries against actual source validators`
71
+ );
72
+ process.exit(0);
@@ -50,7 +50,9 @@ provider:
50
50
  INBOX_QUEUE_URL: ${env:INBOX_QUEUE_URL, ''}
51
51
  INBOX_QUEUE_ENDPOINT: ${env:INBOX_QUEUE_ENDPOINT, ''}
52
52
  PORT: ${env:PORT, 3000}
53
- LAMBDA_STAGE: ${opt:stage, "dev"}
53
+ CONFIG_STAGE: ${env:CONFIG_STAGE, ''}
54
+ # serverless-offline sets AWS_LAMBDA_FUNCTION_NAME, so bootstrap reads LAMBDA_STAGE.
55
+ LAMBDA_STAGE: ${env:CONFIG_STAGE, ''}
54
56
  SEED: ${env:SEED}
55
57
  NEO4J_URI: ${env:NEO4J_URI}
56
58
  NEO4J_USERNAME: ${env:NEO4J_USERNAME}
@@ -61,17 +63,12 @@ provider:
61
63
  'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Port]
62
64
  NOTIFICATIONS_QUEUE_URL: ${construct:notifications-queue.queueUrl}
63
65
  SKILL_EMBEDDING_GOOGLE_API_KEY: ${env:SKILL_EMBEDDING_GOOGLE_API_KEY, ""}
64
- SKILL_EMBEDDING_GOOGLE_MODEL: ${env:SKILL_EMBEDDING_GOOGLE_MODEL, "gemini-embedding-001"}
65
66
  SKILL_EMBEDDING_BATCH_SIZE: ${env:SKILL_EMBEDDING_BATCH_SIZE, "25"}
66
67
  SKILL_EMBEDDING_BACKFILL_ON_STARTUP: ${env:SKILL_EMBEDDING_BACKFILL_ON_STARTUP, "false"}
67
68
  SKILL_EMBEDDING_BACKFILL_LOCK_TTL_SECONDS: ${env:SKILL_EMBEDDING_BACKFILL_LOCK_TTL_SECONDS, "900"}
68
69
  SKILL_EMBEDDING_BACKFILL_PAGE_SIZE: ${env:SKILL_EMBEDDING_BACKFILL_PAGE_SIZE, "100"}
69
70
  SKILL_EMBEDDING_BACKFILL_BATCH_SIZE: ${env:SKILL_EMBEDDING_BACKFILL_BATCH_SIZE, "50"}
70
- OIDC_TRUSTED_ISSUERS: ${env:OIDC_TRUSTED_ISSUERS, ""}
71
- OIDC_EXPECTED_AUDIENCE: ${env:OIDC_EXPECTED_AUDIENCE, ""}
72
- CREDENTIAL_REFRESH_ENABLED: ${env:CREDENTIAL_REFRESH_ENABLED, "false"}
73
71
  CREDENTIAL_REFRESH_DIGEST_SECRET: ${env:CREDENTIAL_REFRESH_DIGEST_SECRET, ""}
74
- CREDENTIAL_REFRESH_NOTIFICATION_WINDOW_HOURS: ${env:CREDENTIAL_REFRESH_NOTIFICATION_WINDOW_HOURS, "24"}
75
72
  tracing:
76
73
  apiGateway: true
77
74
  lambda: true
@@ -0,0 +1,50 @@
1
+ const { functionEnvironment, pickNonEmpty } = require('@learncard/service-config/function-env.cjs');
2
+
3
+ // User-approved brain-service deployment credentials. In bundle mode these are delivered
4
+ // through the runtime-secrets bundle and omitted here; in fallback mode (no bundle) the
5
+ // existing per-value GitHub environment secrets are retained. Only genuine credentials
6
+ // belong in this list — non-secret allowlists, tuning flags and per-stage values are
7
+ // delivered through checked-in stage config at cold start, never forwarded here.
8
+ const PROVIDER_SECRETS = [
9
+ 'SEED',
10
+ 'NEO4J_URI',
11
+ 'NEO4J_USERNAME',
12
+ 'NEO4J_PASSWORD',
13
+ 'POSTMARK_API_KEY',
14
+ 'MESSAGEBIRD_AUTH_TOKEN',
15
+ 'SENTRY_DSN',
16
+ 'POSTHOG_API_KEY',
17
+ 'SKILL_EMBEDDING_GOOGLE_API_KEY',
18
+ 'SKILLS_PROVIDER_API_KEY',
19
+ 'TWILIO_ACCOUNT_SID',
20
+ 'TWILIO_AUTH_TOKEN',
21
+ 'SMART_RESUME_CLIENT_ID',
22
+ 'SMART_RESUME_ACCESS_KEY',
23
+ 'SMART_RESUME_CONTRACT_URI',
24
+ 'CREDENTIAL_REFRESH_DIGEST_SECRET',
25
+ 'SHARE_LINK_REQUEST_HASH_SECRET',
26
+ 'APP_STORE_ADMIN_PROFILE_IDS',
27
+ 'LOGIN_PROVIDER_DID',
28
+ 'NOTIFICATIONS_SERVICE_WEBHOOK_URL',
29
+ ];
30
+
31
+ // Serverless v3 supplies CLI options to file functions and recursively resolves the strings
32
+ // it returns, so CloudFormation intrinsics (Fn::GetAtt) and construct references survive.
33
+ // Every brain-service function shares the one generated execution role, so the bundle pointer
34
+ // and the credential fallbacks sit together on the provider. Bundle mode emits only infra plus
35
+ // the pointer; fallback mode retains the per-value GitHub secrets. Non-secret per-stage tuning
36
+ // and allowlists are applied from checked-in stage config at cold start, never forwarded here.
37
+ exports.provider = ({ options = {}, env = process.env } = {}) => ({
38
+ LAMBDA_STAGE: options.stage || 'dev',
39
+ // Non-secret product selector for the shared stage files (config.<tenant>.<stage>.json).
40
+ CONFIG_TENANT: env.CONFIG_TENANT || 'learncard',
41
+ PORT: String(options.httpPort || '3000'),
42
+ REDIS_HOST: { 'Fn::GetAtt': ['ElasticCacheCluster', 'RedisEndpoint.Address'] },
43
+ REDIS_PORT: { 'Fn::GetAtt': ['ElasticCacheCluster', 'RedisEndpoint.Port'] },
44
+ NOTIFICATIONS_QUEUE_URL: '${construct:notifications-queue.queueUrl}',
45
+ INBOX_QUEUE_URL: '${construct:inbox-queue.queueUrl}',
46
+ // The deploy commit, emitted only when set so an empty string never counts toward the
47
+ // 4KB per-function environment limit.
48
+ ...pickNonEmpty(['GIT_SHA'], env),
49
+ ...functionEnvironment({ always: ['RUNTIME_SECRETS_ID'], fallback: PROVIDER_SECRETS, env }),
50
+ });
@@ -64,7 +64,6 @@ provider:
64
64
  'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Address]
65
65
  REDIS_PORT:
66
66
  'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Port]
67
- APP_BRAND: ${env:APP_BRAND, 'scouts'}
68
67
  tracing:
69
68
  apiGateway: true
70
69
  lambda: true