@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,273 @@
1
+ import type {
2
+ ShareContentActiveSummary,
3
+ ShareContentContentProjection,
4
+ ShareContentDeleteValue,
5
+ ShareContentEnvelope,
6
+ ShareContentOwnerRecovery,
7
+ ShareContentPutValue,
8
+ ShareContentRecoveryProjection,
9
+ ShareContentStatValue,
10
+ ShareContentTombstoneSummary,
11
+ } from './types';
12
+
13
+ /**
14
+ * Narrow local validators for the LearnCloud JSON response envelope. They are
15
+ * intentionally hand-written (rather than reusing a shared schema) so the
16
+ * transport contract is pinned here: every accepted field is checked, unknown
17
+ * fields are rejected, and no opaque payload is ever coerced or rewritten.
18
+ *
19
+ * Dates arrive as ISO-8601 strings because the C2 repository's `Date` values are
20
+ * JSON-serialized by the route.
21
+ */
22
+
23
+ const isPlainObject = (value: unknown): value is Record<string, unknown> =>
24
+ typeof value === 'object' &&
25
+ value !== null &&
26
+ !Array.isArray(value) &&
27
+ (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
28
+
29
+ const isNonEmptyString = (value: unknown): value is string =>
30
+ typeof value === 'string' && value.length > 0;
31
+
32
+ const isSafeIntegerAtLeast = (value: unknown, minimum: number): value is number =>
33
+ typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum;
34
+
35
+ const isSha256Hex = (value: unknown): value is string =>
36
+ typeof value === 'string' && /^[0-9a-f]{64}$/.test(value);
37
+
38
+ const hasExactKeys = (value: Record<string, unknown>, keys: readonly string[]): boolean => {
39
+ const actual = Object.keys(value);
40
+
41
+ return actual.length === keys.length && actual.every(key => keys.includes(key));
42
+ };
43
+
44
+ const parseEnvelope = (value: unknown): ShareContentEnvelope | null => {
45
+ if (!isPlainObject(value) || !hasExactKeys(value, ['v', 'alg', 'iv', 'ct'])) return null;
46
+ if (value.v !== 1 || value.alg !== 'A256GCM') return null;
47
+ if (!isNonEmptyString(value.iv) || !isNonEmptyString(value.ct)) return null;
48
+
49
+ return { v: 1, alg: 'A256GCM', iv: value.iv, ct: value.ct };
50
+ };
51
+
52
+ const parseOwnerRecovery = (value: unknown): ShareContentOwnerRecovery | null =>
53
+ isPlainObject(value) ? { ...value } : null;
54
+
55
+ const parseTupleFields = (
56
+ value: Record<string, unknown>
57
+ ): {
58
+ namespace: string;
59
+ ownerProfileId: string;
60
+ shareId: string;
61
+ contentVersion: number;
62
+ objectId: string;
63
+ operationId: string;
64
+ } | null => {
65
+ if (!isNonEmptyString(value.namespace)) return null;
66
+ if (!isNonEmptyString(value.ownerProfileId)) return null;
67
+ if (!isNonEmptyString(value.shareId)) return null;
68
+ if (!isSafeIntegerAtLeast(value.contentVersion, 1)) return null;
69
+ if (!isNonEmptyString(value.objectId)) return null;
70
+ if (!isNonEmptyString(value.operationId)) return null;
71
+
72
+ return {
73
+ namespace: value.namespace,
74
+ ownerProfileId: value.ownerProfileId,
75
+ shareId: value.shareId,
76
+ contentVersion: value.contentVersion,
77
+ objectId: value.objectId,
78
+ operationId: value.operationId,
79
+ };
80
+ };
81
+
82
+ const parseActiveSummary = (value: unknown): ShareContentActiveSummary | null => {
83
+ if (!isPlainObject(value) || value.kind !== 'active') return null;
84
+ if (
85
+ !hasExactKeys(value, [
86
+ 'kind',
87
+ 'namespace',
88
+ 'ownerProfileId',
89
+ 'shareId',
90
+ 'contentVersion',
91
+ 'objectId',
92
+ 'operationId',
93
+ 'contentHash',
94
+ 'payloadHash',
95
+ 'ciphertextBytes',
96
+ 'recoveryBytes',
97
+ 'createdAt',
98
+ ])
99
+ )
100
+ return null;
101
+
102
+ const tuple = parseTupleFields(value);
103
+
104
+ if (!tuple) return null;
105
+ if (!isSha256Hex(value.contentHash) || !isSha256Hex(value.payloadHash)) return null;
106
+ if (!isSafeIntegerAtLeast(value.ciphertextBytes, 0)) return null;
107
+ if (!isSafeIntegerAtLeast(value.recoveryBytes, 0)) return null;
108
+ if (!isNonEmptyString(value.createdAt)) return null;
109
+
110
+ return {
111
+ kind: 'active',
112
+ ...tuple,
113
+ contentHash: value.contentHash,
114
+ payloadHash: value.payloadHash,
115
+ ciphertextBytes: value.ciphertextBytes,
116
+ recoveryBytes: value.recoveryBytes,
117
+ createdAt: value.createdAt,
118
+ };
119
+ };
120
+
121
+ const parseTombstoneSummary = (value: unknown): ShareContentTombstoneSummary | null => {
122
+ if (!isPlainObject(value) || value.kind !== 'tombstone') return null;
123
+
124
+ const allowed = [
125
+ 'kind',
126
+ 'namespace',
127
+ 'ownerProfileId',
128
+ 'shareId',
129
+ 'contentVersion',
130
+ 'objectId',
131
+ 'operationId',
132
+ 'contentHash',
133
+ 'deletedAt',
134
+ ];
135
+
136
+ if (Object.keys(value).some(key => !allowed.includes(key))) return null;
137
+
138
+ const tuple = parseTupleFields(value);
139
+
140
+ if (!tuple) return null;
141
+ if (value.contentHash !== undefined && !isSha256Hex(value.contentHash)) return null;
142
+ if (!isNonEmptyString(value.deletedAt)) return null;
143
+
144
+ return {
145
+ kind: 'tombstone',
146
+ ...tuple,
147
+ ...(value.contentHash !== undefined ? { contentHash: value.contentHash } : {}),
148
+ deletedAt: value.deletedAt,
149
+ };
150
+ };
151
+
152
+ export const parseShareContentActiveSummary = parseActiveSummary;
153
+ export const parseShareContentTombstoneSummary = parseTombstoneSummary;
154
+
155
+ export const parseShareContentStatValue = (value: unknown): ShareContentStatValue | null =>
156
+ parseActiveSummary(value) ?? parseTombstoneSummary(value);
157
+
158
+ export const parseShareContentDeleteValue = (value: unknown): ShareContentDeleteValue | null =>
159
+ parseTombstoneSummary(value);
160
+
161
+ export const parseShareContentPutValue = (value: unknown): ShareContentPutValue | null => {
162
+ if (!isPlainObject(value) || !hasExactKeys(value, ['status', 'record'])) return null;
163
+ if (value.status !== 'created' && value.status !== 'idempotent') return null;
164
+
165
+ const record = parseActiveSummary(value.record);
166
+
167
+ if (!record) return null;
168
+
169
+ return { status: value.status, record };
170
+ };
171
+
172
+ export const parseShareContentContentProjection = (
173
+ value: unknown
174
+ ): ShareContentContentProjection | null => {
175
+ if (!isPlainObject(value) || value.kind !== 'active') return null;
176
+ if (
177
+ !hasExactKeys(value, [
178
+ 'kind',
179
+ 'namespace',
180
+ 'ownerProfileId',
181
+ 'shareId',
182
+ 'contentVersion',
183
+ 'objectId',
184
+ 'operationId',
185
+ 'contentHash',
186
+ 'payloadHash',
187
+ 'envelope',
188
+ 'ciphertextBytes',
189
+ 'createdAt',
190
+ ])
191
+ )
192
+ return null;
193
+
194
+ const tuple = parseTupleFields(value);
195
+ const envelope = parseEnvelope(value.envelope);
196
+
197
+ if (!tuple || !envelope) return null;
198
+ if (!isSha256Hex(value.contentHash) || !isSha256Hex(value.payloadHash)) return null;
199
+ if (!isSafeIntegerAtLeast(value.ciphertextBytes, 0)) return null;
200
+ if (!isNonEmptyString(value.createdAt)) return null;
201
+
202
+ return {
203
+ kind: 'active',
204
+ ...tuple,
205
+ contentHash: value.contentHash,
206
+ payloadHash: value.payloadHash,
207
+ envelope,
208
+ ciphertextBytes: value.ciphertextBytes,
209
+ createdAt: value.createdAt,
210
+ };
211
+ };
212
+
213
+ export const parseShareContentRecoveryProjection = (
214
+ value: unknown
215
+ ): ShareContentRecoveryProjection | null => {
216
+ if (!isPlainObject(value) || value.kind !== 'active') return null;
217
+ if (
218
+ !hasExactKeys(value, [
219
+ 'kind',
220
+ 'namespace',
221
+ 'ownerProfileId',
222
+ 'shareId',
223
+ 'contentVersion',
224
+ 'objectId',
225
+ 'operationId',
226
+ 'contentHash',
227
+ 'payloadHash',
228
+ 'ownerEncryptedRecovery',
229
+ 'recoveryBytes',
230
+ 'createdAt',
231
+ ])
232
+ )
233
+ return null;
234
+
235
+ const tuple = parseTupleFields(value);
236
+ const recovery = parseOwnerRecovery(value.ownerEncryptedRecovery);
237
+
238
+ if (!tuple || !recovery) return null;
239
+ if (!isSha256Hex(value.contentHash) || !isSha256Hex(value.payloadHash)) return null;
240
+ if (!isSafeIntegerAtLeast(value.recoveryBytes, 0)) return null;
241
+ if (!isNonEmptyString(value.createdAt)) return null;
242
+
243
+ return {
244
+ kind: 'active',
245
+ ...tuple,
246
+ contentHash: value.contentHash,
247
+ payloadHash: value.payloadHash,
248
+ ownerEncryptedRecovery: recovery,
249
+ recoveryBytes: value.recoveryBytes,
250
+ createdAt: value.createdAt,
251
+ };
252
+ };
253
+
254
+ /** Parse the fixed `{ok:true,value}` / `{ok:false,error}` JSON envelope. */
255
+ export const parseShareContentResponseEnvelope = (
256
+ parsed: unknown
257
+ ): { ok: true; value: unknown } | { ok: false; error: string } | null => {
258
+ if (!isPlainObject(parsed)) return null;
259
+
260
+ if (parsed.ok === true) {
261
+ if (!('value' in parsed)) return null;
262
+
263
+ return { ok: true, value: parsed.value };
264
+ }
265
+
266
+ if (parsed.ok === false) {
267
+ if (typeof parsed.error !== 'string' || parsed.error.length === 0) return null;
268
+
269
+ return { ok: false, error: parsed.error };
270
+ }
271
+
272
+ return null;
273
+ };
@@ -0,0 +1,253 @@
1
+ /**
2
+ * LC-2187 Brain → LearnCloud share-content HTTP client types.
3
+ *
4
+ * This module defines the *transport* grammar for the fixed server-to-server
5
+ * contract. It is deliberately local to brain-service: production must never
6
+ * import LearnCloud runtime code, so the few shared literals (the six-field
7
+ * tuple, the operation names, the request-hash canon) are restated here and
8
+ * pinned by cross-implementation contract tests.
9
+ *
10
+ * The client never persists or logs the ciphertext, the owner recovery JWE, the
11
+ * signed token or the claims. Every value that crosses this boundary is either a
12
+ * non-secret binding or an already-encrypted opaque payload.
13
+ */
14
+
15
+ /**
16
+ * The exact operation allowlist. The operation is carried by the route, not the
17
+ * request body, so it is a client-side parameter, never a body field.
18
+ */
19
+ export const SHARE_CONTENT_OPERATIONS = ['put', 'get', 'stat', 'delete', 'readRecovery'] as const;
20
+
21
+ export type ShareContentOperation = (typeof SHARE_CONTENT_OPERATIONS)[number];
22
+
23
+ /** Signed purpose claim accepted by the C1 verifier. */
24
+ export const SHARE_CONTENT_AUTH_PURPOSE = 'lc-share-content/v1' as const;
25
+
26
+ /** Bound on the complete JSON request body. */
27
+ export const DEFAULT_MAX_SHARE_CONTENT_REQUEST_BYTES = 1024 * 1024;
28
+ /** Bound on the complete JSON response body. */
29
+ export const DEFAULT_MAX_SHARE_CONTENT_RESPONSE_BYTES = 2 * 1024 * 1024;
30
+ /** Maximum signed token lifetime in seconds; the contract caps this at 60. */
31
+ export const MAX_SHARE_CONTENT_TOKEN_TTL_SECONDS = 60;
32
+ export const DEFAULT_SHARE_CONTENT_REQUEST_TIMEOUT_MS = 10_000;
33
+ export const DEFAULT_SHARE_CONTENT_MAX_ATTEMPTS = 3;
34
+ export const DEFAULT_SHARE_CONTENT_RETRY_BACKOFF_MS = 200;
35
+ export const MAX_SHARE_CONTENT_RETRY_BACKOFF_MS = 5_000;
36
+
37
+ /** A256GCM envelope exactly as stored by LearnCloud. */
38
+ export type ShareContentEnvelope = {
39
+ v: 1;
40
+ alg: 'A256GCM';
41
+ iv: string;
42
+ ct: string;
43
+ };
44
+
45
+ /** Opaque owner-encrypted recovery JWE. Never interpreted by this module. */
46
+ export type ShareContentOwnerRecovery = Record<string, unknown>;
47
+
48
+ /**
49
+ * The exact six-field immutable tuple. Every request — including `put` — carries
50
+ * these fields and the route supplies the operation.
51
+ */
52
+ export type ShareContentTuple = {
53
+ namespace: string;
54
+ ownerProfileId: string;
55
+ shareId: string;
56
+ contentVersion: number;
57
+ objectId: string;
58
+ operationId: string;
59
+ };
60
+
61
+ export type ShareContentPutRequest = ShareContentTuple & {
62
+ envelope: ShareContentEnvelope;
63
+ ownerEncryptedRecovery: ShareContentOwnerRecovery;
64
+ };
65
+
66
+ /** Claims signed into the JWT-VP `nonce`; mirrors the reviewed C1 claim list. */
67
+ export type ShareContentAuthorizationClaims = {
68
+ iss: string;
69
+ aud: string;
70
+ purpose: typeof SHARE_CONTENT_AUTH_PURPOSE;
71
+ namespace: string;
72
+ op: ShareContentOperation;
73
+ shareId: string;
74
+ contentVersion: number;
75
+ ownerProfileId: string;
76
+ objectId: string;
77
+ operationId: string;
78
+ requestHash: string;
79
+ iat: number;
80
+ exp: number;
81
+ jti: string;
82
+ };
83
+
84
+ /**
85
+ * Injected signing adapter. The runtime implementation wraps the existing
86
+ * did:web LearnCard primitives; tests inject a fake or a checked-in DIDKit
87
+ * fixture. `claims` already contains a fresh CSPRNG `jti` and bounded times.
88
+ */
89
+ export type ShareContentTokenSigner = (claims: ShareContentAuthorizationClaims) => Promise<string>;
90
+
91
+ /** Response value: active object summary (no ciphertext or recovery). */
92
+ export type ShareContentActiveSummary = {
93
+ kind: 'active';
94
+ namespace: string;
95
+ ownerProfileId: string;
96
+ shareId: string;
97
+ contentVersion: number;
98
+ objectId: string;
99
+ operationId: string;
100
+ contentHash: string;
101
+ payloadHash: string;
102
+ ciphertextBytes: number;
103
+ recoveryBytes: number;
104
+ createdAt: string;
105
+ };
106
+
107
+ /** Response value: permanent tombstone summary. */
108
+ export type ShareContentTombstoneSummary = {
109
+ kind: 'tombstone';
110
+ namespace: string;
111
+ ownerProfileId: string;
112
+ shareId: string;
113
+ contentVersion: number;
114
+ objectId: string;
115
+ operationId: string;
116
+ contentHash?: string;
117
+ deletedAt: string;
118
+ };
119
+
120
+ export type ShareContentStatValue = ShareContentActiveSummary | ShareContentTombstoneSummary;
121
+
122
+ /** Response value for `put`. */
123
+ export type ShareContentPutValue = {
124
+ status: 'created' | 'idempotent';
125
+ record: ShareContentActiveSummary;
126
+ };
127
+
128
+ /** Response value for `get`: content projection, never owner recovery. */
129
+ export type ShareContentContentProjection = {
130
+ kind: 'active';
131
+ namespace: string;
132
+ ownerProfileId: string;
133
+ shareId: string;
134
+ contentVersion: number;
135
+ objectId: string;
136
+ operationId: string;
137
+ contentHash: string;
138
+ payloadHash: string;
139
+ envelope: ShareContentEnvelope;
140
+ ciphertextBytes: number;
141
+ createdAt: string;
142
+ };
143
+
144
+ /** Response value for `readRecovery`: owner-only projection, never ciphertext. */
145
+ export type ShareContentRecoveryProjection = {
146
+ kind: 'active';
147
+ namespace: string;
148
+ ownerProfileId: string;
149
+ shareId: string;
150
+ contentVersion: number;
151
+ objectId: string;
152
+ operationId: string;
153
+ contentHash: string;
154
+ payloadHash: string;
155
+ ownerEncryptedRecovery: ShareContentOwnerRecovery;
156
+ recoveryBytes: number;
157
+ createdAt: string;
158
+ };
159
+
160
+ /** Response value for `delete`: the permanent tombstone. */
161
+ export type ShareContentDeleteValue = ShareContentTombstoneSummary;
162
+
163
+ /**
164
+ * Stable, non-secret transport/repository codes. Routes and callers must map
165
+ * every one to a generic external result; none of them carry token or body data.
166
+ */
167
+ export type ShareContentClientErrorCode =
168
+ | 'DISABLED'
169
+ | 'INVALID_INPUT'
170
+ | 'UNAUTHORIZED'
171
+ | 'NOT_FOUND'
172
+ | 'CONFLICT'
173
+ | 'PAYLOAD_TOO_LARGE'
174
+ | 'UNAVAILABLE'
175
+ | 'UNEXPECTED_STATUS'
176
+ | 'MALFORMED_RESPONSE'
177
+ | 'RESPONSE_TOO_LARGE'
178
+ | 'RESPONSE_TUPLE_MISMATCH'
179
+ | 'REDIRECT_REJECTED'
180
+ | 'TIMEOUT'
181
+ | 'NETWORK_ERROR'
182
+ | 'SIGNING_FAILED';
183
+
184
+ export type ShareContentClientResult<T> =
185
+ { ok: true; value: T } | { ok: false; error: ShareContentClientErrorCode };
186
+
187
+ /** Transport errors that a bounded retry (with a fresh token/jti) may re-attempt. */
188
+ export const TRANSIENT_SHARE_CONTENT_ERRORS: readonly ShareContentClientErrorCode[] = [
189
+ 'UNAVAILABLE',
190
+ 'TIMEOUT',
191
+ 'NETWORK_ERROR',
192
+ ];
193
+
194
+ export const isTransientShareContentError = (code: ShareContentClientErrorCode): boolean =>
195
+ TRANSIENT_SHARE_CONTENT_ERRORS.includes(code);
196
+
197
+ /** Configuration/validation failure raised before any network I/O. */
198
+ export class ShareContentClientConfigurationError extends Error {
199
+ readonly code = 'SHARE_CONTENT_CLIENT_CONFIGURATION_ERROR';
200
+
201
+ constructor(message: string) {
202
+ super(message);
203
+ this.name = 'ShareContentClientConfigurationError';
204
+ }
205
+ }
206
+
207
+ export type ShareContentClientConfig = {
208
+ /** Fail-closed switch. When false every operation returns `DISABLED`. */
209
+ enabled: boolean;
210
+ /** Trusted origin, e.g. `https://learncloud.example`. No path/query/userinfo. */
211
+ origin: string;
212
+ /** Explicit stable opaque namespace accepted by C1/C2 (no `%`/`:`). */
213
+ namespace: string;
214
+ /** LearnCloud service DID (the token audience). */
215
+ audience: string;
216
+ /** Brain service DID the signer is expected to sign as. */
217
+ signerDid: string;
218
+ /**
219
+ * When true, a loopback `http://localhost`/`127.0.0.1`/`[::1]` origin is
220
+ * accepted for local tests/development. The cryptographic auth path is
221
+ * identical; there is never an unsigned bypass.
222
+ */
223
+ allowInsecureLoopback: boolean;
224
+ signer: ShareContentTokenSigner;
225
+ requestTimeoutMs: number;
226
+ maxAttempts: number;
227
+ retryBackoffMs: number;
228
+ maxRequestBytes: number;
229
+ maxResponseBytes: number;
230
+ /** Epoch-milliseconds clock, injectable for deterministic tests. */
231
+ now: () => number;
232
+ /** Bound sleep, injectable so retry tests do not wait. */
233
+ sleep: (ms: number) => Promise<void>;
234
+ /** Injectable fetch for tests; defaults to the global fetch. */
235
+ fetchImpl: typeof fetch;
236
+ };
237
+
238
+ export type ShareContentClient = {
239
+ readonly config: ShareContentClientConfig;
240
+ put: (
241
+ request: ShareContentPutRequest
242
+ ) => Promise<ShareContentClientResult<ShareContentPutValue>>;
243
+ get: (
244
+ request: ShareContentTuple
245
+ ) => Promise<ShareContentClientResult<ShareContentContentProjection>>;
246
+ stat: (request: ShareContentTuple) => Promise<ShareContentClientResult<ShareContentStatValue>>;
247
+ delete: (
248
+ request: ShareContentTuple
249
+ ) => Promise<ShareContentClientResult<ShareContentDeleteValue>>;
250
+ readRecovery: (
251
+ request: ShareContentTuple
252
+ ) => Promise<ShareContentClientResult<ShareContentRecoveryProjection>>;
253
+ };
@@ -0,0 +1,87 @@
1
+ import { createHmac, randomUUID, timingSafeEqual } from 'node:crypto';
2
+ import { getShareLinkRequestHashSecret } from './share-link-lifecycle';
3
+ import type { ShareLinkRecord } from '../models/ShareLink';
4
+
5
+ const TTL_MS = 60 * 1000;
6
+ const sign = (text: string): string =>
7
+ createHmac('sha256', getShareLinkRequestHashSecret()).update(text).digest('base64url');
8
+ const binding = (record: ShareLinkRecord, namespace: string, sourceIp?: string): string =>
9
+ sign(
10
+ JSON.stringify([
11
+ 'attachment-access-v1',
12
+ namespace,
13
+ record.ownerProfileId,
14
+ record.id,
15
+ record.contentVersion,
16
+ record.attachmentId,
17
+ record.attachmentChunkCount,
18
+ record.passcodeHash ?? null,
19
+ sourceIp ?? null,
20
+ ])
21
+ );
22
+
23
+ /** Opaque keyed binding never exposes the password verifier or internal owner/namespace. */
24
+ export const mintShareAttachmentAccessToken = (
25
+ record: ShareLinkRecord,
26
+ namespace: string,
27
+ sourceIp: string | undefined,
28
+ now: Date
29
+ ): string => {
30
+ const body = Buffer.from(
31
+ JSON.stringify({
32
+ v: 1,
33
+ expiresAt: now.getTime() + TTL_MS,
34
+ nonce: randomUUID(),
35
+ binding: binding(record, namespace, sourceIp),
36
+ })
37
+ ).toString('base64url');
38
+ return `${body}.${sign(`attachment-grant-v1:${body}`)}`;
39
+ };
40
+
41
+ /** A grant only amortizes a successful password check; active/current authorization remains mandatory. */
42
+ export const verifyShareAttachmentAccessToken = (
43
+ token: string,
44
+ record: ShareLinkRecord,
45
+ namespace: string,
46
+ sourceIp: string | undefined,
47
+ now: Date
48
+ ): boolean => {
49
+ try {
50
+ const parts = token.split('.');
51
+ const bodyPart = parts[0];
52
+ const signature = parts[1];
53
+ if (
54
+ parts.length !== 2 ||
55
+ !bodyPart ||
56
+ !signature ||
57
+ signature.length !== 43 ||
58
+ token.length > 2048
59
+ )
60
+ return false;
61
+ const bytes = Buffer.from(bodyPart, 'base64url');
62
+ if (bytes.toString('base64url') !== bodyPart) return false;
63
+ const actual = Buffer.from(signature, 'base64url');
64
+ const expected = Buffer.from(sign(`attachment-grant-v1:${bodyPart}`), 'base64url');
65
+ if (
66
+ actual.length !== expected.length ||
67
+ actual.toString('base64url') !== signature ||
68
+ !timingSafeEqual(actual, expected)
69
+ )
70
+ return false;
71
+ const body: unknown = JSON.parse(bytes.toString('utf8'));
72
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return false;
73
+ const value = body as Record<string, unknown>;
74
+ if (
75
+ value.v !== 1 ||
76
+ typeof value.expiresAt !== 'number' ||
77
+ !Number.isSafeInteger(value.expiresAt) ||
78
+ value.expiresAt <= now.getTime() ||
79
+ value.expiresAt > now.getTime() + TTL_MS ||
80
+ value.binding !== binding(record, namespace, sourceIp)
81
+ )
82
+ return false;
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ };