life-as-api 0.2.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 (303) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -0
  3. package/dist/agent/finance-agent.d.ts +44 -0
  4. package/dist/agent/finance-agent.js +75 -0
  5. package/dist/agent/finance-agent.js.map +1 -0
  6. package/dist/agent/legal-agent.d.ts +44 -0
  7. package/dist/agent/legal-agent.js +76 -0
  8. package/dist/agent/legal-agent.js.map +1 -0
  9. package/dist/agent/medical-agent.d.ts +44 -0
  10. package/dist/agent/medical-agent.js +75 -0
  11. package/dist/agent/medical-agent.js.map +1 -0
  12. package/dist/agent/trusted-agent-registry.d.ts +53 -0
  13. package/dist/agent/trusted-agent-registry.js +57 -0
  14. package/dist/agent/trusted-agent-registry.js.map +1 -0
  15. package/dist/audit/audit-chain-verifier.d.ts +10 -0
  16. package/dist/audit/audit-chain-verifier.js +67 -0
  17. package/dist/audit/audit-chain-verifier.js.map +1 -0
  18. package/dist/audit/audit-log.d.ts +48 -0
  19. package/dist/audit/audit-log.js +74 -0
  20. package/dist/audit/audit-log.js.map +1 -0
  21. package/dist/audit/sqlite-audit-log.d.ts +29 -0
  22. package/dist/audit/sqlite-audit-log.js +142 -0
  23. package/dist/audit/sqlite-audit-log.js.map +1 -0
  24. package/dist/auth/access-context-issuer.d.ts +18 -0
  25. package/dist/auth/access-context-issuer.js +88 -0
  26. package/dist/auth/access-context-issuer.js.map +1 -0
  27. package/dist/auth/assert-authorized-for-subject.d.ts +2 -0
  28. package/dist/auth/assert-authorized-for-subject.js +20 -0
  29. package/dist/auth/assert-authorized-for-subject.js.map +1 -0
  30. package/dist/auth/backend-context-minting-boundary.d.ts +23 -0
  31. package/dist/auth/backend-context-minting-boundary.js +281 -0
  32. package/dist/auth/backend-context-minting-boundary.js.map +1 -0
  33. package/dist/auth/default-authorization-policy.d.ts +7 -0
  34. package/dist/auth/default-authorization-policy.js +64 -0
  35. package/dist/auth/default-authorization-policy.js.map +1 -0
  36. package/dist/auth/fake-auth-adapter.d.ts +7 -0
  37. package/dist/auth/fake-auth-adapter.js +27 -0
  38. package/dist/auth/fake-auth-adapter.js.map +1 -0
  39. package/dist/auth/http-client.d.ts +12 -0
  40. package/dist/auth/http-client.js +7 -0
  41. package/dist/auth/http-client.js.map +1 -0
  42. package/dist/auth/in-memory-grant-resolver.d.ts +21 -0
  43. package/dist/auth/in-memory-grant-resolver.js +73 -0
  44. package/dist/auth/in-memory-grant-resolver.js.map +1 -0
  45. package/dist/auth/in-memory-tenant-boundary.d.ts +16 -0
  46. package/dist/auth/in-memory-tenant-boundary.js +37 -0
  47. package/dist/auth/in-memory-tenant-boundary.js.map +1 -0
  48. package/dist/auth/jwks-key-ring-auth-adapter.d.ts +38 -0
  49. package/dist/auth/jwks-key-ring-auth-adapter.js +123 -0
  50. package/dist/auth/jwks-key-ring-auth-adapter.js.map +1 -0
  51. package/dist/auth/jwks-key-ring.d.ts +25 -0
  52. package/dist/auth/jwks-key-ring.js +111 -0
  53. package/dist/auth/jwks-key-ring.js.map +1 -0
  54. package/dist/auth/jwt-auth-adapter.d.ts +15 -0
  55. package/dist/auth/jwt-auth-adapter.js +59 -0
  56. package/dist/auth/jwt-auth-adapter.js.map +1 -0
  57. package/dist/auth/jwt-core.d.ts +32 -0
  58. package/dist/auth/jwt-core.js +226 -0
  59. package/dist/auth/jwt-core.js.map +1 -0
  60. package/dist/auth/jwt-key-ring-auth-adapter.d.ts +16 -0
  61. package/dist/auth/jwt-key-ring-auth-adapter.js +58 -0
  62. package/dist/auth/jwt-key-ring-auth-adapter.js.map +1 -0
  63. package/dist/auth/oidc-discovery.d.ts +15 -0
  64. package/dist/auth/oidc-discovery.js +46 -0
  65. package/dist/auth/oidc-discovery.js.map +1 -0
  66. package/dist/auth/production-scoped-life-api.d.ts +58 -0
  67. package/dist/auth/production-scoped-life-api.js +802 -0
  68. package/dist/auth/production-scoped-life-api.js.map +1 -0
  69. package/dist/auth/proposal-scope-inspector.d.ts +3 -0
  70. package/dist/auth/proposal-scope-inspector.js +42 -0
  71. package/dist/auth/proposal-scope-inspector.js.map +1 -0
  72. package/dist/auth/proposal-scoped-life-api.d.ts +21 -0
  73. package/dist/auth/proposal-scoped-life-api.js +168 -0
  74. package/dist/auth/proposal-scoped-life-api.js.map +1 -0
  75. package/dist/auth/rate-limited-context-minting-boundary.d.ts +27 -0
  76. package/dist/auth/rate-limited-context-minting-boundary.js +77 -0
  77. package/dist/auth/rate-limited-context-minting-boundary.js.map +1 -0
  78. package/dist/auth/revocation-aware-auth-adapter.d.ts +21 -0
  79. package/dist/auth/revocation-aware-auth-adapter.js +88 -0
  80. package/dist/auth/revocation-aware-auth-adapter.js.map +1 -0
  81. package/dist/auth/sqlite-grant-resolver.d.ts +8 -0
  82. package/dist/auth/sqlite-grant-resolver.js +78 -0
  83. package/dist/auth/sqlite-grant-resolver.js.map +1 -0
  84. package/dist/auth/sqlite-tenant-boundary.d.ts +7 -0
  85. package/dist/auth/sqlite-tenant-boundary.js +41 -0
  86. package/dist/auth/sqlite-tenant-boundary.js.map +1 -0
  87. package/dist/auth/static-jwt-key-ring.d.ts +13 -0
  88. package/dist/auth/static-jwt-key-ring.js +73 -0
  89. package/dist/auth/static-jwt-key-ring.js.map +1 -0
  90. package/dist/auth/subject-scoped-life-api.d.ts +14 -0
  91. package/dist/auth/subject-scoped-life-api.js +51 -0
  92. package/dist/auth/subject-scoped-life-api.js.map +1 -0
  93. package/dist/auth/test-context-minting-boundary.d.ts +13 -0
  94. package/dist/auth/test-context-minting-boundary.js +74 -0
  95. package/dist/auth/test-context-minting-boundary.js.map +1 -0
  96. package/dist/auth/types.d.ts +77 -0
  97. package/dist/auth/types.js +16 -0
  98. package/dist/auth/types.js.map +1 -0
  99. package/dist/compliance/subject-data-report.d.ts +43 -0
  100. package/dist/compliance/subject-data-report.js +155 -0
  101. package/dist/compliance/subject-data-report.js.map +1 -0
  102. package/dist/consent/consent-guard.d.ts +4 -0
  103. package/dist/consent/consent-guard.js +37 -0
  104. package/dist/consent/consent-guard.js.map +1 -0
  105. package/dist/consent/grant-registry.d.ts +7 -0
  106. package/dist/consent/grant-registry.js +13 -0
  107. package/dist/consent/grant-registry.js.map +1 -0
  108. package/dist/consent/intersect-selector.d.ts +3 -0
  109. package/dist/consent/intersect-selector.js +66 -0
  110. package/dist/consent/intersect-selector.js.map +1 -0
  111. package/dist/contract/diagnostics.d.ts +32 -0
  112. package/dist/contract/diagnostics.js +112 -0
  113. package/dist/contract/diagnostics.js.map +1 -0
  114. package/dist/contract/project-contracts.d.ts +5 -0
  115. package/dist/contract/project-contracts.js +159 -0
  116. package/dist/contract/project-contracts.js.map +1 -0
  117. package/dist/contract/types.d.ts +32 -0
  118. package/dist/contract/types.js +5 -0
  119. package/dist/contract/types.js.map +1 -0
  120. package/dist/crypto/aes-gcm.d.ts +11 -0
  121. package/dist/crypto/aes-gcm.js +78 -0
  122. package/dist/crypto/aes-gcm.js.map +1 -0
  123. package/dist/crypto/claim-serializer.d.ts +26 -0
  124. package/dist/crypto/claim-serializer.js +59 -0
  125. package/dist/crypto/claim-serializer.js.map +1 -0
  126. package/dist/crypto/sha256.d.ts +12 -0
  127. package/dist/crypto/sha256.js +40 -0
  128. package/dist/crypto/sha256.js.map +1 -0
  129. package/dist/crypto/static-key.d.ts +11 -0
  130. package/dist/crypto/static-key.js +60 -0
  131. package/dist/crypto/static-key.js.map +1 -0
  132. package/dist/crypto/types.d.ts +20 -0
  133. package/dist/crypto/types.js +8 -0
  134. package/dist/crypto/types.js.map +1 -0
  135. package/dist/datasource/trusted-data-source-registry.d.ts +51 -0
  136. package/dist/datasource/trusted-data-source-registry.js +57 -0
  137. package/dist/datasource/trusted-data-source-registry.js.map +1 -0
  138. package/dist/erasure/tombstone.d.ts +13 -0
  139. package/dist/erasure/tombstone.js +20 -0
  140. package/dist/erasure/tombstone.js.map +1 -0
  141. package/dist/evidence/evidence-backed-claim-creator.d.ts +48 -0
  142. package/dist/evidence/evidence-backed-claim-creator.js +107 -0
  143. package/dist/evidence/evidence-backed-claim-creator.js.map +1 -0
  144. package/dist/evidence/in-memory-raw-document-store.d.ts +26 -0
  145. package/dist/evidence/in-memory-raw-document-store.js +84 -0
  146. package/dist/evidence/in-memory-raw-document-store.js.map +1 -0
  147. package/dist/evidence/raw-document-store.d.ts +12 -0
  148. package/dist/evidence/raw-document-store.js +8 -0
  149. package/dist/evidence/raw-document-store.js.map +1 -0
  150. package/dist/evidence/source-evidence-validator.d.ts +4 -0
  151. package/dist/evidence/source-evidence-validator.js +14 -0
  152. package/dist/evidence/source-evidence-validator.js.map +1 -0
  153. package/dist/evidence/sqlite-raw-document-store.d.ts +19 -0
  154. package/dist/evidence/sqlite-raw-document-store.js +122 -0
  155. package/dist/evidence/sqlite-raw-document-store.js.map +1 -0
  156. package/dist/guardian/guardian-registry.d.ts +96 -0
  157. package/dist/guardian/guardian-registry.js +95 -0
  158. package/dist/guardian/guardian-registry.js.map +1 -0
  159. package/dist/index.d.ts +44 -0
  160. package/dist/index.js +72 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/observability/health.d.ts +12 -0
  163. package/dist/observability/health.js +16 -0
  164. package/dist/observability/health.js.map +1 -0
  165. package/dist/observability/logger.d.ts +21 -0
  166. package/dist/observability/logger.js +40 -0
  167. package/dist/observability/logger.js.map +1 -0
  168. package/dist/observability/metrics.d.ts +15 -0
  169. package/dist/observability/metrics.js +52 -0
  170. package/dist/observability/metrics.js.map +1 -0
  171. package/dist/persistence/backup.d.ts +24 -0
  172. package/dist/persistence/backup.js +251 -0
  173. package/dist/persistence/backup.js.map +1 -0
  174. package/dist/persistence/decryptability-scan.d.ts +10 -0
  175. package/dist/persistence/decryptability-scan.js +144 -0
  176. package/dist/persistence/decryptability-scan.js.map +1 -0
  177. package/dist/persistence/in-memory-unit-of-work.d.ts +11 -0
  178. package/dist/persistence/in-memory-unit-of-work.js +32 -0
  179. package/dist/persistence/in-memory-unit-of-work.js.map +1 -0
  180. package/dist/persistence/key-availability.d.ts +9 -0
  181. package/dist/persistence/key-availability.js +125 -0
  182. package/dist/persistence/key-availability.js.map +1 -0
  183. package/dist/persistence/sqlite/migrations.d.ts +9 -0
  184. package/dist/persistence/sqlite/migrations.js +535 -0
  185. package/dist/persistence/sqlite/migrations.js.map +1 -0
  186. package/dist/persistence/sqlite/schema.d.ts +10 -0
  187. package/dist/persistence/sqlite/schema.js +101 -0
  188. package/dist/persistence/sqlite/schema.js.map +1 -0
  189. package/dist/persistence/sqlite/sqlite-fact-store.d.ts +24 -0
  190. package/dist/persistence/sqlite/sqlite-fact-store.js +207 -0
  191. package/dist/persistence/sqlite/sqlite-fact-store.js.map +1 -0
  192. package/dist/persistence/sqlite/sqlite-source-registry.d.ts +17 -0
  193. package/dist/persistence/sqlite/sqlite-source-registry.js +132 -0
  194. package/dist/persistence/sqlite/sqlite-source-registry.js.map +1 -0
  195. package/dist/persistence/sqlite/sqlite-unit-of-work.d.ts +8 -0
  196. package/dist/persistence/sqlite/sqlite-unit-of-work.js +41 -0
  197. package/dist/persistence/sqlite/sqlite-unit-of-work.js.map +1 -0
  198. package/dist/persistence/sqlite/types.d.ts +24 -0
  199. package/dist/persistence/sqlite/types.js +28 -0
  200. package/dist/persistence/sqlite/types.js.map +1 -0
  201. package/dist/persistence/unit-of-work.d.ts +6 -0
  202. package/dist/persistence/unit-of-work.js +15 -0
  203. package/dist/persistence/unit-of-work.js.map +1 -0
  204. package/dist/proposal/accepted-claim-appender.d.ts +7 -0
  205. package/dist/proposal/accepted-claim-appender.js +16 -0
  206. package/dist/proposal/accepted-claim-appender.js.map +1 -0
  207. package/dist/proposal/in-memory-proposal-queue.d.ts +21 -0
  208. package/dist/proposal/in-memory-proposal-queue.js +218 -0
  209. package/dist/proposal/in-memory-proposal-queue.js.map +1 -0
  210. package/dist/proposal/policy-aware-proposal-queue.d.ts +23 -0
  211. package/dist/proposal/policy-aware-proposal-queue.js +162 -0
  212. package/dist/proposal/policy-aware-proposal-queue.js.map +1 -0
  213. package/dist/proposal/proposal-policy.d.ts +37 -0
  214. package/dist/proposal/proposal-policy.js +72 -0
  215. package/dist/proposal/proposal-policy.js.map +1 -0
  216. package/dist/proposal/proposal-queue.d.ts +13 -0
  217. package/dist/proposal/proposal-queue.js +15 -0
  218. package/dist/proposal/proposal-queue.js.map +1 -0
  219. package/dist/proposal/source-aware-claim-appender.d.ts +13 -0
  220. package/dist/proposal/source-aware-claim-appender.js +47 -0
  221. package/dist/proposal/source-aware-claim-appender.js.map +1 -0
  222. package/dist/proposal/sqlite-proposal-queue.d.ts +26 -0
  223. package/dist/proposal/sqlite-proposal-queue.js +282 -0
  224. package/dist/proposal/sqlite-proposal-queue.js.map +1 -0
  225. package/dist/resolution/resolve-claims.d.ts +4 -0
  226. package/dist/resolution/resolve-claims.js +101 -0
  227. package/dist/resolution/resolve-claims.js.map +1 -0
  228. package/dist/resolution/trust-profile.d.ts +12 -0
  229. package/dist/resolution/trust-profile.js +23 -0
  230. package/dist/resolution/trust-profile.js.map +1 -0
  231. package/dist/resolution/types.d.ts +15 -0
  232. package/dist/resolution/types.js +8 -0
  233. package/dist/resolution/types.js.map +1 -0
  234. package/dist/sdk/create-backend-integrated-life-api-for-test.d.ts +23 -0
  235. package/dist/sdk/create-backend-integrated-life-api-for-test.js +240 -0
  236. package/dist/sdk/create-backend-integrated-life-api-for-test.js.map +1 -0
  237. package/dist/sdk/create-life-api.d.ts +55 -0
  238. package/dist/sdk/create-life-api.js +251 -0
  239. package/dist/sdk/create-life-api.js.map +1 -0
  240. package/dist/sdk/create-sqlite-life-api.d.ts +33 -0
  241. package/dist/sdk/create-sqlite-life-api.js +654 -0
  242. package/dist/sdk/create-sqlite-life-api.js.map +1 -0
  243. package/dist/sdk/life-api.d.ts +27 -0
  244. package/dist/sdk/life-api.js +17 -0
  245. package/dist/sdk/life-api.js.map +1 -0
  246. package/dist/sdk/validation.d.ts +8 -0
  247. package/dist/sdk/validation.js +216 -0
  248. package/dist/sdk/validation.js.map +1 -0
  249. package/dist/source/in-memory-source-registry.d.ts +15 -0
  250. package/dist/source/in-memory-source-registry.js +104 -0
  251. package/dist/source/in-memory-source-registry.js.map +1 -0
  252. package/dist/source/source-aware-fact-store.d.ts +19 -0
  253. package/dist/source/source-aware-fact-store.js +40 -0
  254. package/dist/source/source-aware-fact-store.js.map +1 -0
  255. package/dist/source/source-kind-predicate-policy.d.ts +4 -0
  256. package/dist/source/source-kind-predicate-policy.js +62 -0
  257. package/dist/source/source-kind-predicate-policy.js.map +1 -0
  258. package/dist/source/source-registry.d.ts +11 -0
  259. package/dist/source/source-registry.js +12 -0
  260. package/dist/source/source-registry.js.map +1 -0
  261. package/dist/store/in-memory-fact-store.d.ts +25 -0
  262. package/dist/store/in-memory-fact-store.js +192 -0
  263. package/dist/store/in-memory-fact-store.js.map +1 -0
  264. package/dist/surface/context-query.d.ts +21 -0
  265. package/dist/surface/context-query.js +113 -0
  266. package/dist/surface/context-query.js.map +1 -0
  267. package/dist/surface/evidence-backed-context-query.d.ts +62 -0
  268. package/dist/surface/evidence-backed-context-query.js +240 -0
  269. package/dist/surface/evidence-backed-context-query.js.map +1 -0
  270. package/dist/types/access-context.d.ts +25 -0
  271. package/dist/types/access-context.js +6 -0
  272. package/dist/types/access-context.js.map +1 -0
  273. package/dist/types/claim.d.ts +48 -0
  274. package/dist/types/claim.js +5 -0
  275. package/dist/types/claim.js.map +1 -0
  276. package/dist/types/consent.d.ts +41 -0
  277. package/dist/types/consent.js +3 -0
  278. package/dist/types/consent.js.map +1 -0
  279. package/dist/types/crypto-boundary.d.ts +12 -0
  280. package/dist/types/crypto-boundary.js +15 -0
  281. package/dist/types/crypto-boundary.js.map +1 -0
  282. package/dist/types/evidence.d.ts +31 -0
  283. package/dist/types/evidence.js +8 -0
  284. package/dist/types/evidence.js.map +1 -0
  285. package/dist/types/fact-store.d.ts +24 -0
  286. package/dist/types/fact-store.js +12 -0
  287. package/dist/types/fact-store.js.map +1 -0
  288. package/dist/types/ids.d.ts +36 -0
  289. package/dist/types/ids.js +11 -0
  290. package/dist/types/ids.js.map +1 -0
  291. package/dist/types/proposal.d.ts +38 -0
  292. package/dist/types/proposal.js +7 -0
  293. package/dist/types/proposal.js.map +1 -0
  294. package/dist/types/source.d.ts +33 -0
  295. package/dist/types/source.js +6 -0
  296. package/dist/types/source.js.map +1 -0
  297. package/dist/types/trust.d.ts +24 -0
  298. package/dist/types/trust.js +5 -0
  299. package/dist/types/trust.js.map +1 -0
  300. package/dist/utilities/deep-freeze.d.ts +1 -0
  301. package/dist/utilities/deep-freeze.js +17 -0
  302. package/dist/utilities/deep-freeze.js.map +1 -0
  303. package/package.json +55 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legal-agent.js","sourceRoot":"","sources":["../../src/agent/legal-agent.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,sFAAsF;AACtF,qFAAqF;AACrF,oFAAoF;AACpF,yBAAyB;AACzB,sEAAsE;AACtE,yFAAyF;AACzF,yFAAyF;AACzF,iFAAiF;AACjF,8EAA8E;AAC9E,kFAAkF;AAClF,0FAA0F;AAC1F,mBAAmB;AACnB,uFAAuF;AACvF,wFAAwF;AACxF,kEAAkE;AAMlE,MAAM,CAAC,MAAM,cAAc,GAAG,4BAA4B,CAAC;AAC3D,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,cAAc,CAAU,CAAC;AAGtH,mFAAmF;AACnF,4EAA4E;AAC5E,MAAM,sCAAsC,GAC1C,oGAAoG,CAAC;AAEvG;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,YAAoB;IAC1D,OAAO;QACL,OAAO,EAAE,cAAc;QACvB,IAAI,EAAE,qCAAqC;QAC3C,WAAW,EAAE,CAAC,GAAG,sBAAsB,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,CAAC;QACzF,WAAW,EAAE,wBAAwB;QACrC,UAAU,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE;QAChD,YAAY,EAAE,sCAAsC;KACrD,CAAC;AACJ,CAAC;AAgBD;;;GAGG;AACH,MAAM,OAAO,UAAU;IACQ;IAA7B,YAA6B,IAAuB;QAAvB,SAAI,GAAJ,IAAI,CAAmB;IAAG,CAAC;IAExD;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,CAAC,GAAkB,EAAE,KAA0B;QACtD,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,CAAC;QACpC,MAAM,OAAO,GACX,OAAO,KAAK,SAAS;YACpB,sBAAgD,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEtE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;YACzB,OAAO,EAAE,cAAc;YACvB,UAAU,EAAE,qBAAqB;YACjC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;YACxC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CACP,6EAA6E;gBAC7E,iFAAiF,OAAO,IAAI,WAAW,GAAG,CAC3G,EACD,EAAE,IAAI,EAAE,8BAA8B,EAAE,CACzC,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ import type { Clock, MintedContext } from "../auth/types.js";
2
+ import type { QueryContractsInput, ContextQueryResult } from "../surface/evidence-backed-context-query.js";
3
+ import type { TrustedAgentRegistryEntry } from "./trusted-agent-registry.js";
4
+ export declare const MEDICAL_AGENT_ID = "life-as-api/medical-agent-v1";
5
+ export declare const MEDICAL_AGENT_VERSION = "1.0.0";
6
+ export declare const ALLOWED_MEDICAL_PURPOSES: readonly ["diagnosis", "treatment", "emergency"];
7
+ export type MedicalPurpose = typeof ALLOWED_MEDICAL_PURPOSES[number];
8
+ /**
9
+ * Builds the TrustedAgentRegistryEntry for the certified Medical Agent.
10
+ * MEDICAL_AGENT_REGISTRY_ENTRY_001: certifiedBy is always "life-as-api-foundation".
11
+ * Permissions include every allowed purpose (the minting boundary passes
12
+ * decision.purpose as the scope argument to isAgentTrusted) plus declarative
13
+ * read:* scopes for operator documentation.
14
+ */
15
+ export declare function medicalAgentRegistryEntry(validUntilMs: number): TrustedAgentRegistryEntry;
16
+ export interface MedicalAgentAuditEvent {
17
+ readonly at: string;
18
+ readonly agentId: string;
19
+ readonly reasonCode: "MEDICAL_AGENT_RUN_001";
20
+ readonly decision: "allowed" | "denied";
21
+ readonly declaredPurpose?: string;
22
+ }
23
+ export interface MedicalAgentOptions {
24
+ readonly query: (ctx: MintedContext, input: QueryContractsInput) => Promise<ContextQueryResult>;
25
+ readonly clock: Clock;
26
+ readonly onAudit?: (event: MedicalAgentAuditEvent) => void;
27
+ }
28
+ /**
29
+ * Certified first-party Medical Agent.
30
+ * MEDICAL_AGENT_READ_ONLY_001: query-capability only, no propose/write capability.
31
+ */
32
+ export declare class MedicalAgent {
33
+ private readonly opts;
34
+ constructor(opts: MedicalAgentOptions);
35
+ /**
36
+ * MEDICAL_AGENT_SCOPE_ENFORCED_001 / MEDICAL_AGENT_PURPOSE_BOUND_001: run() only
37
+ * delegates to the configured query function when ctx.declaredPurpose is one of
38
+ * ALLOWED_MEDICAL_PURPOSES. Any other value, or an absent declaredPurpose, denies
39
+ * fail-closed before the query function is ever invoked.
40
+ * MEDICAL_AGENT_AUDIT_TRAIL_001: the onAudit hook (if configured) always fires,
41
+ * for both the allowed and denied outcome.
42
+ */
43
+ run(ctx: MintedContext, query: QueryContractsInput): Promise<ContextQueryResult>;
44
+ }
@@ -0,0 +1,75 @@
1
+ // Platform-05 — Certified First-Party Agents: Medical Agent.
2
+ //
3
+ // MEDICAL_AGENT_PURPOSE_BOUND_001: this agent acts ONLY when the caller's MintedContext
4
+ // carries declaredPurpose "diagnosis" | "treatment" | "emergency". Any other declared
5
+ // purpose (or none) is denied before the query function is invoked.
6
+ // MEDICAL_AGENT_REGISTRY_ENTRY_001: medicalAgentRegistryEntry() returns a
7
+ // TrustedAgentRegistryEntry certifiedBy "life-as-api-foundation" so hosts can register
8
+ // this agent with a TrustedAgentRegistry (Platform-01) without hand-rolling the entry.
9
+ // MEDICAL_AGENT_SCOPE_ENFORCED_001: run() throws MEDICAL_AGENT_SCOPE_DENIED_001 when
10
+ // declaredPurpose is absent or not in ALLOWED_MEDICAL_PURPOSES — fail-closed.
11
+ // MEDICAL_AGENT_READ_ONLY_001: the agent has query-capability only. The constructor
12
+ // accepts exclusively a `query` function; there is no propose/write capability anywhere
13
+ // on this class.
14
+ // MEDICAL_AGENT_AUDIT_TRAIL_001: every run() call invokes the optional onAudit hook with
15
+ // reasonCode "MEDICAL_AGENT_RUN_001" (allowed or denied). Actual audit persistence is a
16
+ // host responsibility — this hook is a notification point only.
17
+ export const MEDICAL_AGENT_ID = "life-as-api/medical-agent-v1";
18
+ export const MEDICAL_AGENT_VERSION = "1.0.0";
19
+ export const ALLOWED_MEDICAL_PURPOSES = ["diagnosis", "treatment", "emergency"];
20
+ // TRUSTED_AGENT_REGISTRY_PUBKEY_ENFORCEMENT_DEFERRED_001: placeholder public key —
21
+ // cryptographic signature verification at mint time is not yet implemented.
22
+ const MEDICAL_AGENT_PUBLIC_KEY_PEM_PLACEHOLDER = "-----BEGIN PUBLIC KEY-----\nPLACEHOLDER-MEDICAL-AGENT-KEY-NOT-YET-ENFORCED\n-----END PUBLIC KEY-----";
23
+ /**
24
+ * Builds the TrustedAgentRegistryEntry for the certified Medical Agent.
25
+ * MEDICAL_AGENT_REGISTRY_ENTRY_001: certifiedBy is always "life-as-api-foundation".
26
+ * Permissions include every allowed purpose (the minting boundary passes
27
+ * decision.purpose as the scope argument to isAgentTrusted) plus declarative
28
+ * read:* scopes for operator documentation.
29
+ */
30
+ export function medicalAgentRegistryEntry(validUntilMs) {
31
+ return {
32
+ agentId: MEDICAL_AGENT_ID,
33
+ name: "Certified Medical Agent (first-party)",
34
+ permissions: [...ALLOWED_MEDICAL_PURPOSES, "read:health", "read:diagnosis", "read:treatment"],
35
+ certifiedBy: "life-as-api-foundation",
36
+ validUntil: new Date(validUntilMs).toISOString(),
37
+ publicKeyPem: MEDICAL_AGENT_PUBLIC_KEY_PEM_PLACEHOLDER,
38
+ };
39
+ }
40
+ /**
41
+ * Certified first-party Medical Agent.
42
+ * MEDICAL_AGENT_READ_ONLY_001: query-capability only, no propose/write capability.
43
+ */
44
+ export class MedicalAgent {
45
+ opts;
46
+ constructor(opts) {
47
+ this.opts = opts;
48
+ }
49
+ /**
50
+ * MEDICAL_AGENT_SCOPE_ENFORCED_001 / MEDICAL_AGENT_PURPOSE_BOUND_001: run() only
51
+ * delegates to the configured query function when ctx.declaredPurpose is one of
52
+ * ALLOWED_MEDICAL_PURPOSES. Any other value, or an absent declaredPurpose, denies
53
+ * fail-closed before the query function is ever invoked.
54
+ * MEDICAL_AGENT_AUDIT_TRAIL_001: the onAudit hook (if configured) always fires,
55
+ * for both the allowed and denied outcome.
56
+ */
57
+ async run(ctx, query) {
58
+ const purpose = ctx.declaredPurpose;
59
+ const allowed = purpose !== undefined &&
60
+ ALLOWED_MEDICAL_PURPOSES.includes(purpose);
61
+ this.opts.onAudit?.({
62
+ at: this.opts.clock.now(),
63
+ agentId: MEDICAL_AGENT_ID,
64
+ reasonCode: "MEDICAL_AGENT_RUN_001",
65
+ decision: allowed ? "allowed" : "denied",
66
+ ...(purpose !== undefined ? { declaredPurpose: purpose } : {}),
67
+ });
68
+ if (!allowed) {
69
+ throw Object.assign(new Error("MEDICAL_AGENT_SCOPE_DENIED_001: MedicalAgent may only run with declaredPurpose " +
70
+ `"diagnosis" | "treatment" | "emergency" (got: ${purpose ?? "undefined"})`), { code: "MEDICAL_AGENT_SCOPE_DENIED_001" });
71
+ }
72
+ return this.opts.query(ctx, query);
73
+ }
74
+ }
75
+ //# sourceMappingURL=medical-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"medical-agent.js","sourceRoot":"","sources":["../../src/agent/medical-agent.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,wFAAwF;AACxF,wFAAwF;AACxF,sEAAsE;AACtE,0EAA0E;AAC1E,yFAAyF;AACzF,yFAAyF;AACzF,qFAAqF;AACrF,gFAAgF;AAChF,oFAAoF;AACpF,0FAA0F;AAC1F,mBAAmB;AACnB,yFAAyF;AACzF,0FAA0F;AAC1F,kEAAkE;AAMlE,MAAM,CAAC,MAAM,gBAAgB,GAAG,8BAA8B,CAAC;AAC/D,MAAM,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC;AAE7C,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,CAAU,CAAC;AAGzF,mFAAmF;AACnF,4EAA4E;AAC5E,MAAM,wCAAwC,GAC5C,sGAAsG,CAAC;AAEzG;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,YAAoB;IAC5D,OAAO;QACL,OAAO,EAAE,gBAAgB;QACzB,IAAI,EAAE,uCAAuC;QAC7C,WAAW,EAAE,CAAC,GAAG,wBAAwB,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,CAAC;QAC7F,WAAW,EAAE,wBAAwB;QACrC,UAAU,EAAE,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE;QAChD,YAAY,EAAE,wCAAwC;KACvD,CAAC;AACJ,CAAC;AAgBD;;;GAGG;AACH,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,IAAyB;QAAzB,SAAI,GAAJ,IAAI,CAAqB;IAAG,CAAC;IAE1D;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,CAAC,GAAkB,EAAE,KAA0B;QACtD,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,CAAC;QACpC,MAAM,OAAO,GACX,OAAO,KAAK,SAAS;YACpB,wBAAkD,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAExE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;YACzB,OAAO,EAAE,gBAAgB;YACzB,UAAU,EAAE,uBAAuB;YACnC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;YACxC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/D,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CACP,iFAAiF;gBACjF,iDAAiD,OAAO,IAAI,WAAW,GAAG,CAC3E,EACD,EAAE,IAAI,EAAE,gCAAgC,EAAE,CAC3C,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;CACF"}
@@ -0,0 +1,53 @@
1
+ /** A registry record for a certified agent. */
2
+ export interface TrustedAgentRegistryEntry {
3
+ /** Stable agent identifier — matches VerifiedPrincipal.principalId for agent principals. */
4
+ readonly agentId: string;
5
+ /** Human-readable name for operator reference. */
6
+ readonly name: string;
7
+ /**
8
+ * Scope strings this agent is certified for. Vocabulary is host-defined.
9
+ * The minting boundary passes decision.purpose (a PurposeTag) as the scope argument
10
+ * to isAgentTrusted; entries should include the relevant PurposeTag values.
11
+ * Fine-grained data scopes (e.g. "read:emergency") belong in ConsentGrant.scope
12
+ * (predicate allow-list) and may additionally be declared here for documentation.
13
+ */
14
+ readonly permissions: ReadonlyArray<string>;
15
+ /** ID of the certifying authority (institution, CA, operator). */
16
+ readonly certifiedBy: string;
17
+ /** ISO-8601 instant after which this certification is no longer valid. */
18
+ readonly validUntil: string;
19
+ /**
20
+ * PEM public key for cryptographic agent identity proof (challenge-response).
21
+ * Declared now; enforcement (signature verification at mint time) is deferred.
22
+ * TRUSTED_AGENT_REGISTRY_PUBKEY_ENFORCEMENT_DEFERRED_001
23
+ */
24
+ readonly publicKeyPem: string;
25
+ }
26
+ /**
27
+ * Trust gate for agent-context minting.
28
+ * TRUSTED_AGENT_REGISTRY_GATES_GRANT_001: configured → checked before every AgentConsentContext.
29
+ * Implementations must be fail-closed: any exception → treated as untrusted.
30
+ */
31
+ export interface TrustedAgentRegistry {
32
+ /** Returns true iff agentId is registered, not expired, and certified for scope. */
33
+ isAgentTrusted(agentId: string, scope: string): Promise<boolean>;
34
+ }
35
+ /** Development / test implementation of TrustedAgentRegistry. */
36
+ export declare class InMemoryTrustedAgentRegistry implements TrustedAgentRegistry {
37
+ private readonly _entries;
38
+ private readonly _now;
39
+ constructor(opts?: {
40
+ readonly now?: () => string;
41
+ });
42
+ /** Register or replace an entry. Re-registering the same agentId overwrites. */
43
+ register(entry: TrustedAgentRegistryEntry): this;
44
+ /**
45
+ * TRUSTED_AGENT_REGISTRY_FAIL_CLOSED_001:
46
+ * - Unknown agentId → false
47
+ * - validUntil unparseable → false
48
+ * - validUntil instant <= now instant (epoch-ms comparison) → false
49
+ * - scope not in permissions → false
50
+ * - Never throws; caller catches and treats exception as untrusted anyway.
51
+ */
52
+ isAgentTrusted(agentId: string, scope: string): Promise<boolean>;
53
+ }
@@ -0,0 +1,57 @@
1
+ // Platform-01 — Trusted Agent Registry.
2
+ //
3
+ // TRUSTED_AGENT_REGISTRY_GATES_GRANT_001: When a TrustedAgentRegistry is configured in
4
+ // the minting boundary, no AgentConsentContext is minted without isAgentTrusted(agentId,
5
+ // scope) returning true. Fail-closed: throws or unknown → denied.
6
+ // AGENT_SCOPE_CHECKED_BEFORE_DATA_ACCESS_001: The registry scope-check fires in the
7
+ // minting boundary BEFORE issuer.mintAgent — no context exists that could access data
8
+ // if the check fails.
9
+ // TRUSTED_AGENT_REGISTRY_FAIL_CLOSED_001: Any registry exception, unknown agentId,
10
+ // unrecognised scope, or expired certification → deny. Never fail-open.
11
+ // TRUSTED_AGENT_REGISTRY_OPTIONAL_NO_CHECK_WHEN_ABSENT_001: Without a configured
12
+ // registry the agent-mint path is unchanged (backward-compatible).
13
+ // TRUSTED_AGENT_REGISTRY_INTERFACE_AGENT_MODULE_ONLY_001: This interface is defined
14
+ // exclusively in src/agent/; auth/ imports type-only; no implementation is imported
15
+ // by production src code other than the re-export in src/index.ts.
16
+ // ─── InMemory implementation ─────────────────────────────────────────────────────
17
+ /** Development / test implementation of TrustedAgentRegistry. */
18
+ export class InMemoryTrustedAgentRegistry {
19
+ _entries = new Map();
20
+ _now;
21
+ constructor(opts) {
22
+ this._now = opts?.now ?? (() => new Date().toISOString());
23
+ }
24
+ /** Register or replace an entry. Re-registering the same agentId overwrites. */
25
+ register(entry) {
26
+ this._entries.set(entry.agentId, entry);
27
+ return this;
28
+ }
29
+ /**
30
+ * TRUSTED_AGENT_REGISTRY_FAIL_CLOSED_001:
31
+ * - Unknown agentId → false
32
+ * - validUntil unparseable → false
33
+ * - validUntil instant <= now instant (epoch-ms comparison) → false
34
+ * - scope not in permissions → false
35
+ * - Never throws; caller catches and treats exception as untrusted anyway.
36
+ */
37
+ async isAgentTrusted(agentId, scope) {
38
+ const entry = this._entries.get(agentId);
39
+ if (entry === undefined)
40
+ return false;
41
+ // Expiry check via epoch-ms — handles UTC offsets correctly, not lexicographic.
42
+ const validUntilMs = Date.parse(entry.validUntil);
43
+ if (Number.isNaN(validUntilMs))
44
+ return false;
45
+ const nowMs = Date.parse(this._now());
46
+ if (Number.isNaN(nowMs))
47
+ return false;
48
+ // Expired: validUntil instant is in the past or exactly now.
49
+ if (validUntilMs <= nowMs)
50
+ return false;
51
+ // Scope check: exact match in permissions array.
52
+ if (!entry.permissions.includes(scope))
53
+ return false;
54
+ return true;
55
+ }
56
+ }
57
+ //# sourceMappingURL=trusted-agent-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trusted-agent-registry.js","sourceRoot":"","sources":["../../src/agent/trusted-agent-registry.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,uFAAuF;AACvF,2FAA2F;AAC3F,oEAAoE;AACpE,oFAAoF;AACpF,wFAAwF;AACxF,wBAAwB;AACxB,mFAAmF;AACnF,0EAA0E;AAC1E,iFAAiF;AACjF,qEAAqE;AACrE,oFAAoF;AACpF,sFAAsF;AACtF,qEAAqE;AA0CrE,oFAAoF;AAEpF,iEAAiE;AACjE,MAAM,OAAO,4BAA4B;IACtB,QAAQ,GAAG,IAAI,GAAG,EAAqC,CAAC;IACxD,IAAI,CAAe;IAEpC,YAAY,IAAsC;QAChD,IAAI,CAAC,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,gFAAgF;IAChF,QAAQ,CAAC,KAAgC;QACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,cAAc,CAAC,OAAe,EAAE,KAAa;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAEtC,gFAAgF;QAChF,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC;YAAE,OAAO,KAAK,CAAC;QAE7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAEtC,6DAA6D;QAC7D,IAAI,YAAY,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QAExC,iDAAiD;QACjD,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAErD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,10 @@
1
+ import type { Database } from "better-sqlite3";
2
+ export type AuditChainInvalidReason = "sequence_gap" | "event_hash_mismatch" | "prev_hash_mismatch" | "event_hash_and_prev_hash_mismatch";
3
+ export type AuditChainVerifyResult = {
4
+ readonly valid: true;
5
+ } | {
6
+ readonly valid: false;
7
+ readonly firstInvalidSequence: number;
8
+ readonly reason: AuditChainInvalidReason;
9
+ };
10
+ export declare function verifyAuditChain(db: Database): AuditChainVerifyResult;
@@ -0,0 +1,67 @@
1
+ // Audit chain verifier — Audit Hardening C4 (Local Hash Chain).
2
+ //
3
+ // PRODUCTION_AUDIT_C4_VERIFIER_SANITIZED_001: result carries only `valid`, an integer
4
+ // `firstInvalidSequence`, and a closed-enum `reason`. NO event payload, actor, selector,
5
+ // reason_code values, IDs, hash values, Error.message, or stack trace ever appear.
6
+ // PRODUCTION_AUDIT_C4_LOCAL_ONLY_NOT_COURT_GRADE_001: this is a lightweight LOCAL consistency
7
+ // check. It detects row deletion, reordering, and naive field modification after the fact.
8
+ // It is a local-only integrity check, NOT external tamper evidence — a privileged attacker
9
+ // with DB write access and SHA-256 can reconstruct a valid chain.
10
+ //
11
+ // Internal utility (Q1): named export from this module ONLY. NOT root-exported from
12
+ // src/index.ts, NOT on ProductionApiBundle / ProductionScopedLifeApi, NOT wired into the
13
+ // bundle's health-check path (offline operator use only).
14
+ import { sha256hexSync } from "../crypto/sha256.js";
15
+ import { buildChainCanonical } from "./sqlite-audit-log.js";
16
+ export function verifyAuditChain(db) {
17
+ const rows = db
18
+ .prepare(`SELECT sequence, prev_hash, event_hash,
19
+ id, recorded_at, actor, grant_id, purpose, decision, reason_code,
20
+ selector_fingerprint, result_count, result_claim_ids, contract_ids
21
+ FROM audit_events ORDER BY sequence ASC`)
22
+ .all();
23
+ if (rows.length === 0)
24
+ return { valid: true };
25
+ for (let i = 0; i < rows.length; i++) {
26
+ const row = rows[i];
27
+ const expectedSeq = i + 1;
28
+ // Sequence continuity (1-indexed, no gaps). Catches deletion / insertion / reorder.
29
+ if (row.sequence !== expectedSeq) {
30
+ return { valid: false, firstInvalidSequence: row.sequence, reason: "sequence_gap" };
31
+ }
32
+ const canonical = buildChainCanonical({
33
+ actor: row.actor,
34
+ contract_ids: row.contract_ids,
35
+ decision: row.decision,
36
+ grant_id: row.grant_id,
37
+ id: row.id,
38
+ prev_hash: row.prev_hash,
39
+ purpose: row.purpose,
40
+ reason_code: row.reason_code,
41
+ recorded_at: row.recorded_at,
42
+ result_claim_ids: row.result_claim_ids,
43
+ result_count: row.result_count,
44
+ selector_fingerprint: row.selector_fingerprint,
45
+ sequence: row.sequence,
46
+ });
47
+ const recomputedHash = sha256hexSync(canonical);
48
+ const hashOk = recomputedHash === row.event_hash;
49
+ // Genesis (i === 0): prev_hash must be null. Otherwise: link to prior row's event_hash.
50
+ const linkOk = i === 0 ? row.prev_hash === null : row.prev_hash === rows[i - 1].event_hash;
51
+ if (!hashOk && !linkOk) {
52
+ return {
53
+ valid: false,
54
+ firstInvalidSequence: row.sequence,
55
+ reason: "event_hash_and_prev_hash_mismatch",
56
+ };
57
+ }
58
+ if (!hashOk) {
59
+ return { valid: false, firstInvalidSequence: row.sequence, reason: "event_hash_mismatch" };
60
+ }
61
+ if (!linkOk) {
62
+ return { valid: false, firstInvalidSequence: row.sequence, reason: "prev_hash_mismatch" };
63
+ }
64
+ }
65
+ return { valid: true };
66
+ }
67
+ //# sourceMappingURL=audit-chain-verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-chain-verifier.js","sourceRoot":"","sources":["../../src/audit/audit-chain-verifier.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,sFAAsF;AACtF,2FAA2F;AAC3F,qFAAqF;AACrF,8FAA8F;AAC9F,6FAA6F;AAC7F,6FAA6F;AAC7F,oEAAoE;AACpE,EAAE;AACF,oFAAoF;AACpF,yFAAyF;AACzF,0DAA0D;AAG1D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAiC5D,MAAM,UAAU,gBAAgB,CAAC,EAAY;IAC3C,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN;;;iDAG2C,CAC5C;SACA,GAAG,EAAgB,CAAC;IAEvB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAE9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACrB,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,CAAC;QAE1B,oFAAoF;QACpF,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YACjC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACtF,CAAC;QAED,MAAM,SAAS,GAAG,mBAAmB,CAAC;YACpC,KAAK,EAAiB,GAAG,CAAC,KAAK;YAC/B,YAAY,EAAU,GAAG,CAAC,YAAY;YACtC,QAAQ,EAAc,GAAG,CAAC,QAA4B;YACtD,QAAQ,EAAc,GAAG,CAAC,QAAQ;YAClC,EAAE,EAAoB,GAAG,CAAC,EAAE;YAC5B,SAAS,EAAa,GAAG,CAAC,SAAS;YACnC,OAAO,EAAe,GAAG,CAAC,OAAO;YACjC,WAAW,EAAW,GAAG,CAAC,WAAW;YACrC,WAAW,EAAW,GAAG,CAAC,WAAW;YACrC,gBAAgB,EAAM,GAAG,CAAC,gBAAgB;YAC1C,YAAY,EAAU,GAAG,CAAC,YAAY;YACtC,oBAAoB,EAAE,GAAG,CAAC,oBAAoB;YAC9C,QAAQ,EAAc,GAAG,CAAC,QAAQ;SACnC,CAAC,CAAC;QACH,MAAM,cAAc,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,cAAc,KAAK,GAAG,CAAC,UAAU,CAAC;QACjD,wFAAwF;QACxF,MAAM,MAAM,GACV,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,UAAU,CAAC;QAE/E,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,oBAAoB,EAAE,GAAG,CAAC,QAAQ;gBAClC,MAAM,EAAE,mCAAmC;aAC5C,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;QAC7F,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,oBAAoB,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QAC5F,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { AuditId, ClaimId, ContractId, GrantId, ISOTime } from "../types/ids.js";
2
+ import type { PurposeTag } from "../types/access-context.js";
3
+ import type { ConsentDenyReason } from "../types/consent.js";
4
+ export type AuditReasonCode = "ALLOWED" | "GRANT_NOT_FOUND" | "GRANT_EXPIRED" | "GRANT_NOT_YET_ACTIVE" | "GRANT_REVOKED" | "PURPOSE_MISMATCH" | "CAPABILITY_NOT_GRANTED" | "SCOPE_INTERSECTION_EMPTY" | "SUBJECT_NOT_GRANTED" | "PROPOSAL_ALLOWED" | "PROPOSAL_CROSS_SUBJECT_DENIED" | "PROPOSAL_BROAD_GRANT_DENIED" | "PROPOSAL_UNSUPPORTED_CONTEXT_DENIED" | "PROPOSAL_MULTI_SUBJECT_DENIED" | "PROPOSAL_EMPTY_CANDIDATES_DENIED" | "PROPOSAL_NOT_FOUND" | "ERASURE_AGENT_DENIED" | "ERASURE_CROSS_SUBJECT_DENIED" | "PROPOSAL_READ_ALLOWED" | "PROPOSAL_READ_SAFE_NOT_FOUND" | "PROPOSAL_READ_AGENT_DENIED" | "PROPOSAL_READ_UNSUPPORTED_CONTEXT_DENIED" | "ERASURE_STATUS_ALLOWED" | "PROPOSAL_AUTO_ACCEPTED_BY_POLICY" | "PROPOSAL_AUTO_REJECTED_BY_POLICY" | "PROPOSAL_AUTO_ACCEPT_FAILED_MANUAL_FALLBACK" | "GUARDIAN_PROPOSAL_ACCEPTED" | "GUARDIAN_GRANT_CREATED" | "GUARDIAN_ACTION_UNAUTHORIZED";
5
+ export declare function denyReasonToAuditCode(r: ConsentDenyReason): AuditReasonCode;
6
+ export interface AuditEvent {
7
+ readonly id: AuditId;
8
+ readonly recordedAt: ISOTime;
9
+ readonly actor: string;
10
+ readonly grantId?: GrantId;
11
+ readonly purpose?: PurposeTag;
12
+ readonly decision: "allow" | "deny";
13
+ readonly reasonCode: AuditReasonCode;
14
+ readonly selectorFingerprint: string;
15
+ readonly resultCount: number;
16
+ readonly resultClaimIds: ReadonlyArray<ClaimId>;
17
+ readonly contractIds?: ReadonlyArray<ContractId>;
18
+ readonly actingAsGuardian?: true;
19
+ readonly wardId?: string;
20
+ }
21
+ export interface AuditLog {
22
+ write(event: AuditEvent): Promise<void>;
23
+ readAll(): ReadonlyArray<AuditEvent>;
24
+ }
25
+ export declare class InMemoryAuditLog implements AuditLog {
26
+ private readonly _events;
27
+ private _failOnWrite;
28
+ simulateWriteFailure(fail: boolean): void;
29
+ write(event: AuditEvent): Promise<void>;
30
+ readAll(): ReadonlyArray<AuditEvent>;
31
+ }
32
+ export declare function buildAuditEvent(params: {
33
+ actor: string;
34
+ grantId?: GrantId;
35
+ purpose?: PurposeTag;
36
+ decision: "allow" | "deny";
37
+ reasonCode: AuditReasonCode;
38
+ selectorFingerprint: string;
39
+ resultCount: number;
40
+ resultClaimIds: ReadonlyArray<ClaimId>;
41
+ contractIds?: ReadonlyArray<ContractId>;
42
+ now: ISOTime;
43
+ actingAsGuardian?: true;
44
+ wardId?: string;
45
+ }): AuditEvent;
46
+ export interface FactoryCompatibleAuditLog extends AuditLog {
47
+ readonly factoryCompatible: "durable-audit-log";
48
+ }
@@ -0,0 +1,74 @@
1
+ // Slice-03: AuditLog — every agent query attempt produces an AuditEvent.
2
+ // AUDIT_EVERY_QUERY_001 — written for allow, deny, empty, expired, revoked
3
+ // AUDIT_FAIL_CLOSED_001 — if write fails, caller must return no data
4
+ // AUDIT_NO_PAYLOADS_001 — AuditEvent contains no claim payload values
5
+ //
6
+ // C1 Audit Coverage codes (Post-MVP Audit Hardening):
7
+ // PRODUCTION_AUDIT_PROPOSAL_MUTATION_RECORDED_001 — allow/deny for createProposal/acceptProposal/rejectProposal
8
+ // PRODUCTION_AUDIT_PROPOSAL_DENIAL_RECORDED_001 — pre-fetch + scope denials
9
+ // PRODUCTION_AUDIT_FACADE_DENIAL_RECORDED_001 — unsupported-context/broad-grant denials
10
+ // PRODUCTION_AUDIT_ERASURE_DENIAL_RECORDED_001 — erasure deny paths
11
+ //
12
+ // C3 Audit Coverage codes (Read Allow-Path Audit):
13
+ // PRODUCTION_AUDIT_READ_ALLOW_PATH_RECORDED_001 — allow paths for getProposal/listProposals/getErasureStatus (fail-closed)
14
+ // PRODUCTION_AUDIT_READ_SAFE_NOT_FOUND_RECORDED_001 — safe-not-found paths for getProposal (best-effort)
15
+ //
16
+ // Platform-02 (PolicyAwareProposalQueue) codes:
17
+ // PROPOSAL_AUTO_ACCEPTED_BY_POLICY_001 — auto-accepted proposals produce a durable audit
18
+ // event with reasonCode PROPOSAL_AUTO_ACCEPTED_BY_POLICY and the resulting claim ids.
19
+ // PROPOSAL_AUTO_REJECTED_BY_POLICY_001 — same for auto-reject, decision "deny".
20
+ // PROPOSAL_POLICY_AUTO_ACCEPT_FAILURE_FALLS_BACK_TO_MANUAL_001 — accept-path failure writes
21
+ // a best-effort deny audit event with reasonCode PROPOSAL_AUTO_ACCEPT_FAILED_MANUAL_FALLBACK.
22
+ import { asAuditId } from "../types/ids.js";
23
+ export function denyReasonToAuditCode(r) {
24
+ const map = {
25
+ grant_not_found: "GRANT_NOT_FOUND",
26
+ grant_expired: "GRANT_EXPIRED",
27
+ grant_not_yet_active: "GRANT_NOT_YET_ACTIVE",
28
+ grant_revoked: "GRANT_REVOKED",
29
+ purpose_mismatch: "PURPOSE_MISMATCH",
30
+ capability_not_granted: "CAPABILITY_NOT_GRANTED",
31
+ scope_empty: "SCOPE_INTERSECTION_EMPTY",
32
+ subject_not_granted: "SUBJECT_NOT_GRANTED",
33
+ };
34
+ return map[r];
35
+ }
36
+ // InMemoryAuditLog — for Slice-03 in-memory tests.
37
+ // Can be configured to throw on write (for AUDIT_FAIL_CLOSED_001 tests).
38
+ export class InMemoryAuditLog {
39
+ _events = [];
40
+ _failOnWrite = false;
41
+ // Test hook: make the next N writes fail
42
+ simulateWriteFailure(fail) {
43
+ this._failOnWrite = fail;
44
+ }
45
+ async write(event) {
46
+ if (this._failOnWrite) {
47
+ throw new Error("InMemoryAuditLog: simulated write failure (AUDIT_FAIL_CLOSED_001 test)");
48
+ }
49
+ this._events.push(event);
50
+ }
51
+ readAll() {
52
+ return [...this._events];
53
+ }
54
+ }
55
+ // AuditEvent factory — ensures consistent shape and prevents payload leakage
56
+ export function buildAuditEvent(params) {
57
+ return {
58
+ id: asAuditId(globalThis.crypto.randomUUID()),
59
+ recordedAt: params.now,
60
+ actor: params.actor,
61
+ decision: params.decision,
62
+ reasonCode: params.reasonCode,
63
+ selectorFingerprint: params.selectorFingerprint,
64
+ resultCount: params.resultCount,
65
+ resultClaimIds: params.resultClaimIds,
66
+ // exactOptionalPropertyTypes: only set optional fields when present
67
+ ...(params.grantId !== undefined ? { grantId: params.grantId } : {}),
68
+ ...(params.purpose !== undefined ? { purpose: params.purpose } : {}),
69
+ ...(params.contractIds !== undefined ? { contractIds: params.contractIds } : {}),
70
+ ...(params.actingAsGuardian === true ? { actingAsGuardian: true } : {}),
71
+ ...(params.wardId !== undefined ? { wardId: params.wardId } : {}),
72
+ };
73
+ }
74
+ //# sourceMappingURL=audit-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-log.js","sourceRoot":"","sources":["../../src/audit/audit-log.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,4EAA4E;AAC5E,sEAAsE;AACtE,uEAAuE;AACvE,EAAE;AACF,sDAAsD;AACtD,gHAAgH;AAChH,8EAA8E;AAC9E,4FAA4F;AAC5F,uEAAuE;AACvE,EAAE;AACF,mDAAmD;AACnD,2HAA2H;AAC3H,yGAAyG;AACzG,EAAE;AACF,gDAAgD;AAChD,yFAAyF;AACzF,wFAAwF;AACxF,gFAAgF;AAChF,4FAA4F;AAC5F,gGAAgG;AAKhG,OAAO,EAAE,SAAS,EAAa,MAAM,iBAAiB,CAAC;AAmCvD,MAAM,UAAU,qBAAqB,CAAC,CAAoB;IACxD,MAAM,GAAG,GAA+C;QACtD,eAAe,EAAW,iBAAiB;QAC3C,aAAa,EAAa,eAAe;QACzC,oBAAoB,EAAM,sBAAsB;QAChD,aAAa,EAAa,eAAe;QACzC,gBAAgB,EAAU,kBAAkB;QAC5C,sBAAsB,EAAI,wBAAwB;QAClD,WAAW,EAAe,0BAA0B;QACpD,mBAAmB,EAAO,qBAAqB;KAChD,CAAC;IACF,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;AAChB,CAAC;AA6BD,mDAAmD;AACnD,yEAAyE;AACzE,MAAM,OAAO,gBAAgB;IACV,OAAO,GAAiB,EAAE,CAAC;IACpC,YAAY,GAAG,KAAK,CAAC;IAE7B,yCAAyC;IACzC,oBAAoB,CAAC,IAAa;QAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAiB;QAC3B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;CACF;AAED,6EAA6E;AAC7E,MAAM,UAAU,eAAe,CAAC,MAc/B;IACC,OAAO;QACL,EAAE,EAAoB,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAC/D,UAAU,EAAY,MAAM,CAAC,GAAG;QAChC,KAAK,EAAiB,MAAM,CAAC,KAAK;QAClC,QAAQ,EAAc,MAAM,CAAC,QAAQ;QACrC,UAAU,EAAY,MAAM,CAAC,UAAU;QACvC,mBAAmB,EAAG,MAAM,CAAC,mBAAmB;QAChD,WAAW,EAAW,MAAM,CAAC,WAAW;QACxC,cAAc,EAAQ,MAAM,CAAC,cAAc;QAC3C,oEAAoE;QACpE,GAAG,CAAC,MAAM,CAAC,OAAO,KAAc,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAW,MAAM,CAAC,OAAO,EAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,MAAM,CAAC,OAAO,KAAc,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAW,MAAM,CAAC,OAAO,EAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,MAAM,CAAC,WAAW,KAAU,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAO,MAAM,CAAC,WAAW,EAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,MAAM,CAAC,gBAAgB,KAAK,IAAI,CAAM,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAa,EAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,GAAG,CAAC,MAAM,CAAC,MAAM,KAAe,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAY,MAAM,CAAC,MAAM,EAAY,CAAC,CAAC,CAAC,EAAE,CAAC;KAChG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ import Database from "better-sqlite3";
2
+ import type { AuditEvent, FactoryCompatibleAuditLog } from "./audit-log.js";
3
+ export interface ChainCanonicalFields {
4
+ readonly actor: string;
5
+ readonly contract_ids: string | null;
6
+ readonly decision: "allow" | "deny";
7
+ readonly grant_id: string | null;
8
+ readonly id: string;
9
+ readonly prev_hash: string | null;
10
+ readonly purpose: string | null;
11
+ readonly reason_code: string;
12
+ readonly recorded_at: string;
13
+ readonly result_claim_ids: string;
14
+ readonly result_count: number;
15
+ readonly selector_fingerprint: string;
16
+ readonly sequence: number;
17
+ readonly acting_as_guardian?: number | null;
18
+ readonly ward_id?: string | null;
19
+ }
20
+ export declare function buildChainCanonical(fields: ChainCanonicalFields): string;
21
+ export declare class SQLiteAuditLog implements FactoryCompatibleAuditLog {
22
+ readonly factoryCompatible: "durable-audit-log";
23
+ private readonly _db;
24
+ private readonly _ownsConnection;
25
+ constructor(dbPathOrDb: string | Database.Database);
26
+ close(): void;
27
+ write(event: AuditEvent): Promise<void>;
28
+ readAll(): ReadonlyArray<AuditEvent>;
29
+ }
@@ -0,0 +1,142 @@
1
+ // SQLiteAuditLog — Slice-17 durable audit sink.
2
+ //
3
+ // AUDIT_DURABLE_SINK_EXISTS_001: implements AuditLog with durable SQLite storage.
4
+ // AUDIT_SQLITE_PERSISTS_EVENTS_001: events survive process exit — stored in the DB file.
5
+ // AUDIT_SQLITE_REOPEN_ROUND_TRIP_001: closing and reopening the DB restores all events.
6
+ // AUDIT_EVENTS_APPEND_ONLY_001: INSERT only — no UPDATE or DELETE methods exist.
7
+ // AUDIT_EVENT_NO_PAYLOAD_VALUES_001: no column in audit_events carries claim payload data.
8
+ // AUDIT_EVENT_NO_RAW_DOCUMENT_BYTES_001: no column carries raw document bytes or decrypted content.
9
+ // AUDIT_EVENT_SELECTOR_FINGERPRINT_ONLY_001: selector_fingerprint is a string summary, not the full selector.
10
+ // AUDIT_SINK_NO_CRYPTO_PRIMITIVES_001: no direct low-level crypto primitive usage here — SHA-256
11
+ // is delegated to the src/crypto/sha256.js helper (CRYPTO_BOUNDARY_ONLY_001).
12
+ // AUDIT_SINK_NO_DOMAIN_MUTATION_001: writes only to audit_events — no FactStore, SourceRegistry, or RawDocumentStore calls.
13
+ //
14
+ // PRODUCTION_AUDIT_C4_HASH_CHAIN_SEQUENCE_001: each write computes a monotonically increasing
15
+ // sequence, a prev_hash link, and a SHA-256 event_hash over the canonical encoding.
16
+ // PRODUCTION_AUDIT_C4_WRITE_ATOMIC_BEGIN_IMMEDIATE_001: write() runs in a BEGIN IMMEDIATE
17
+ // transaction so the read-tip / compute / insert is atomic against concurrent writers.
18
+ import Database from "better-sqlite3";
19
+ import { asAuditId, asClaimId, asContractId, asGrantId, asISOTime } from "../types/ids.js";
20
+ import { initializeSchema } from "../persistence/sqlite/schema.js";
21
+ import { sha256hexSync } from "../crypto/sha256.js";
22
+ export function buildChainCanonical(fields) {
23
+ // Object literal written in alphabetical key order; JSON.stringify preserves insertion order.
24
+ // GUARDIAN_AUDIT_HASH_CHAIN_BACKWARD_COMPATIBLE_001: acting_as_guardian and ward_id are
25
+ // included only when non-null — pre-guardian events produce byte-identical hashes.
26
+ const obj = {
27
+ ...(fields.acting_as_guardian != null ? { acting_as_guardian: fields.acting_as_guardian } : {}),
28
+ actor: fields.actor,
29
+ contract_ids: fields.contract_ids,
30
+ decision: fields.decision,
31
+ grant_id: fields.grant_id,
32
+ id: fields.id,
33
+ prev_hash: fields.prev_hash,
34
+ purpose: fields.purpose,
35
+ reason_code: fields.reason_code,
36
+ recorded_at: fields.recorded_at,
37
+ result_claim_ids: fields.result_claim_ids,
38
+ result_count: fields.result_count,
39
+ selector_fingerprint: fields.selector_fingerprint,
40
+ sequence: fields.sequence,
41
+ ...(fields.ward_id != null ? { ward_id: fields.ward_id } : {}),
42
+ };
43
+ return JSON.stringify(obj);
44
+ }
45
+ function rowToAuditEvent(row) {
46
+ return {
47
+ id: asAuditId(row.id),
48
+ recordedAt: asISOTime(row.recorded_at),
49
+ actor: row.actor,
50
+ decision: row.decision,
51
+ reasonCode: row.reason_code,
52
+ selectorFingerprint: row.selector_fingerprint,
53
+ resultCount: row.result_count,
54
+ resultClaimIds: JSON.parse(row.result_claim_ids).map(id => asClaimId(id)),
55
+ ...(row.grant_id != null ? { grantId: asGrantId(row.grant_id) } : {}),
56
+ ...(row.purpose != null ? { purpose: row.purpose } : {}),
57
+ ...(row.contract_ids != null ? { contractIds: JSON.parse(row.contract_ids).map(id => asContractId(id)) } : {}),
58
+ ...(row.acting_as_guardian === 1 ? { actingAsGuardian: true } : {}),
59
+ ...(row.ward_id != null ? { wardId: row.ward_id } : {}),
60
+ };
61
+ }
62
+ export class SQLiteAuditLog {
63
+ // AUDIT_LOG_FACTORY_COMPATIBLE_REQUIRED_001 / AUDIT_LOG_DURABLE_REQUIRED_001
64
+ factoryCompatible = "durable-audit-log";
65
+ _db;
66
+ _ownsConnection;
67
+ // Accepts a file path (opens own connection) or a shared Database instance.
68
+ // Mirrors SQLiteRawDocumentStore dual-mode pattern (Production-06c).
69
+ constructor(dbPathOrDb) {
70
+ if (typeof dbPathOrDb === "string") {
71
+ this._db = new Database(dbPathOrDb);
72
+ this._ownsConnection = true;
73
+ initializeSchema(this._db);
74
+ }
75
+ else {
76
+ this._db = dbPathOrDb;
77
+ this._ownsConnection = false;
78
+ }
79
+ }
80
+ close() {
81
+ if (this._ownsConnection)
82
+ this._db.close();
83
+ }
84
+ // AUDIT_EVENTS_APPEND_ONLY_001: INSERT only — no UPDATE or DELETE.
85
+ // PRODUCTION_AUDIT_C4_HASH_CHAIN_SEQUENCE_001 / PRODUCTION_AUDIT_C4_PREV_HASH_LINKAGE_001 /
86
+ // PRODUCTION_AUDIT_C4_WRITE_ATOMIC_BEGIN_IMMEDIATE_001: read tip → compute seq/prev/hash →
87
+ // INSERT, all inside one BEGIN IMMEDIATE transaction (synchronous better-sqlite3 callback).
88
+ async write(event) {
89
+ const grantId = event.grantId ?? null;
90
+ const purpose = event.purpose ?? null;
91
+ const resultClaims = JSON.stringify([...event.resultClaimIds]);
92
+ const contractIds = event.contractIds != null ? JSON.stringify([...event.contractIds]) : null;
93
+ // Platform-03 — Guardian columns (GUARDIAN_AUDIT_MARKED_001)
94
+ const actingAsGuardian = event.actingAsGuardian === true ? 1 : null;
95
+ const wardId = event.wardId ?? null;
96
+ const insertChained = this._db.transaction(() => {
97
+ const tip = this._db
98
+ .prepare(`SELECT sequence AS prev_seq, event_hash AS prev_event_hash
99
+ FROM audit_events
100
+ WHERE sequence = (SELECT MAX(sequence) FROM audit_events)`)
101
+ .get();
102
+ const nextSeq = (tip?.prev_seq ?? 0) + 1;
103
+ const prevHash = tip?.prev_event_hash ?? null;
104
+ const canonical = buildChainCanonical({
105
+ actor: event.actor,
106
+ contract_ids: contractIds,
107
+ decision: event.decision,
108
+ grant_id: grantId,
109
+ id: event.id,
110
+ prev_hash: prevHash,
111
+ purpose: purpose,
112
+ reason_code: event.reasonCode,
113
+ recorded_at: event.recordedAt,
114
+ result_claim_ids: resultClaims,
115
+ result_count: event.resultCount,
116
+ selector_fingerprint: event.selectorFingerprint,
117
+ sequence: nextSeq,
118
+ // Guardian fields — null entries omitted from canonical (GUARDIAN_AUDIT_HASH_CHAIN_BACKWARD_COMPATIBLE_001)
119
+ acting_as_guardian: actingAsGuardian,
120
+ ward_id: wardId,
121
+ });
122
+ const eventHash = sha256hexSync(canonical);
123
+ this._db.prepare(`
124
+ INSERT INTO audit_events
125
+ (id, recorded_at, actor, grant_id, purpose, decision, reason_code,
126
+ selector_fingerprint, result_count, result_claim_ids, contract_ids,
127
+ sequence, prev_hash, event_hash, acting_as_guardian, ward_id)
128
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
129
+ `).run(event.id, event.recordedAt, event.actor, grantId, purpose, event.decision, event.reasonCode, event.selectorFingerprint, event.resultCount, resultClaims, contractIds, nextSeq, prevHash, eventHash, actingAsGuardian, wardId);
130
+ });
131
+ // BEGIN IMMEDIATE: reserve the write lock up front so two writers cannot both read the
132
+ // same MAX(sequence). The unique index on sequence is the second safety net.
133
+ insertChained.immediate();
134
+ }
135
+ readAll() {
136
+ return this._db
137
+ .prepare("SELECT * FROM audit_events ORDER BY rowid ASC")
138
+ .all()
139
+ .map(rowToAuditEvent);
140
+ }
141
+ }
142
+ //# sourceMappingURL=sqlite-audit-log.js.map