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,654 @@
1
+ // Production factory — readiness gate + final assembly.
2
+ // Production-01a (AUTH_HOST_ADAPTER_REQUIRED_001)
3
+ // Production-02c-a (AUTH_TENANT_BOUNDARY_REQUIRED_001, AUTH_GRANT_RESOLVER_REQUIRED_001)
4
+ // Production-05-a (db guard, encryptionKey guard, DB-version assertion, readiness-blocked)
5
+ // Production-06d-c-a (async durable ProposalQueue construction proof, ASSEMBLY_DEFERRED_001)
6
+ // Production-06d-d-d (final assembly: steps 15–19, Promise<ProductionApiBundle>)
7
+ //
8
+ // AUTH_HOST_ADAPTER_REQUIRED_001: factory requires an AuthAdapter; missing config or
9
+ // missing authAdapter throws before any other guard fires.
10
+ // AUTH_TENANT_BOUNDARY_REQUIRED_001: requires a TenantBoundary; thrown after authAdapter
11
+ // guard.
12
+ // AUTH_GRANT_RESOLVER_REQUIRED_001: requires a GrantResolver; thrown after tenantBoundary
13
+ // guard.
14
+ // PRODUCTION_FACTORY_DB_REQUIRED_001: requires a pre-migrated Database; thrown after
15
+ // grantResolver guard.
16
+ // PRODUCTION_FACTORY_ENCRYPTION_KEY_REQUIRED_001: requires an encryptionKey; thrown after
17
+ // db guard.
18
+ // PRODUCTION_FACTORY_ENCRYPTION_KEY_TOO_SHORT_001: rawBytes must be >= 32 bytes; thrown
19
+ // after encryptionKey-present check.
20
+ // PRODUCTION_FACTORY_ENCRYPTION_KEY_ID_INVALID_001: keyId must be a non-empty string;
21
+ // thrown after rawBytes length check.
22
+ // PRODUCTION_FACTORY_ENCRYPTION_SOURCE_AMBIGUOUS_001: thrown when both keyProvider and
23
+ // encryptionKey are provided simultaneously. Fail-closed — no partial construction.
24
+ // PRODUCTION_FACTORY_KEY_PROVIDER_INVALID_001: thrown when keyProvider is present but null,
25
+ // non-object, or missing getCurrentKey/getKey methods.
26
+ // PRODUCTION_FACTORY_DB_NOT_READY_001: thrown after keyId check when (a) migration_version
27
+ // is absent or ≠ CURRENT_SCHEMA_VERSION, or (b) verifyCurrentSchema() detects missing
28
+ // tables or columns (e.g. auth_tenant_memberships, auth_grants). migration_version alone
29
+ // is insufficient — structural verification is always required.
30
+ // RAW_DOCUMENT_STORE_FACTORY_COMPATIBLE_REQUIRED_001: rawDocumentStore must be a
31
+ // FactoryCompatibleRawDocumentStore; missing or non-compatible instance throws at step 9
32
+ // before READINESS_BLOCKED (Production-06c).
33
+ // RAW_DOCUMENT_STORE_INMEMORY_REJECTED_001: thrown at step 9.5 when rawDocumentStore lacks
34
+ // the factory-compatible durable marker — defense-in-depth against TS casts.
35
+ // AUDIT_LOG_FACTORY_COMPATIBLE_REQUIRED_001: auditLog must be a FactoryCompatibleAuditLog;
36
+ // missing or non-compatible instance throws at step 10 (Production-06c).
37
+ // AUDIT_LOG_INMEMORY_OR_NOOP_REJECTED_001: thrown at step 10.5 when auditLog lacks the
38
+ // durable marker — InMemoryAuditLog, noop, and test-only sinks not accepted.
39
+ // PRODUCTION_FACTORY_READINESS_BLOCKED_001: historical — was thrown at step 11
40
+ // (Production-06c / Production-05-a) when all core deps were valid but ProposalQueue was
41
+ // not yet wired. Superseded at step 11 by Production-06d-c-a (durable construction proof
42
+ // + PRODUCTION_FACTORY_ASSEMBLY_DEFERRED_001).
43
+ // PRODUCTION_FACTORY_PROPOSAL_QUEUE_COMPATIBLE_REQUIRED_001: thrown at step 11 when the
44
+ // constructed SQLiteProposalQueue does not carry the factoryCompatible marker
45
+ // ("durable-proposal-queue"). Defense-in-depth against casts and non-standard impls.
46
+ // PRODUCTION_FACTORY_ASSEMBLY_DEFERRED_001: superseded by Production-06d-d-d.
47
+ // Was thrown at step 11 after all deps validated. Now replaced by bundle return.
48
+ // See PRODUCTION_FACTORY_ASSEMBLY_DEFERRED_SUPERSEDED_001 in INVARIANTS.md.
49
+ // PRODUCTION_FACTORY_SCOPED_API_TRUST_GATED_001: createScopedApi calls
50
+ // issuer.assertTrusted(mintedCtx.context) before constructing ProductionScopedLifeApiImpl.
51
+ // Rejects manually constructed AccessContext objects and contexts from other factory
52
+ // invocations at capability creation time — not deferred to first method call.
53
+ // PRODUCTION_FACTORY_ISSUER_API_PAIRING_001: AccessContextIssuer and LifeApiImpl are
54
+ // co-created in the same factory call; the issuer WeakSet is private to this invocation.
55
+ // PRODUCTION_FACTORY_RAW_API_NOT_RETURNED_001: LifeApiImpl (rawApi) is sealed in the
56
+ // createScopedApi closure and never returned as a public value.
57
+ // PRODUCTION_FACTORY_ASYNC_DEFERRED_BOUNDARY_001: createSQLiteLifeApiForProduction is
58
+ // async (Promise<ProductionApiBundle>); required by StaticKeyProvider.fromRawBytes
59
+ // (WebCrypto importKey) and SQLiteProposalQueue.open (static async).
60
+ //
61
+ // PRODUCTION_FACTORY_STILL_STUB_001: superseded by Production-05-a. The factory is no
62
+ // longer a stub — it is a readiness gate. See docs/INVARIANTS.md for historical record.
63
+ //
64
+ // Validation order (SPEC-b §6 + Production-06c + Production-06d-c-a):
65
+ // 1. missing config → AUTH_HOST_ADAPTER_REQUIRED_001
66
+ // 2. missing authAdapter → AUTH_HOST_ADAPTER_REQUIRED_001
67
+ // 3. missing tenantBoundary → AUTH_TENANT_BOUNDARY_REQUIRED_001
68
+ // 4. missing grantResolver → AUTH_GRANT_RESOLVER_REQUIRED_001
69
+ // 5. missing db → PRODUCTION_FACTORY_DB_REQUIRED_001
70
+ // 6. both keyProvider and encryptionKey → PRODUCTION_FACTORY_ENCRYPTION_SOURCE_AMBIGUOUS_001
71
+ // 6.5 keyProvider present, invalid → PRODUCTION_FACTORY_KEY_PROVIDER_INVALID_001
72
+ // 6.6 neither provided → PRODUCTION_FACTORY_ENCRYPTION_KEY_REQUIRED_001
73
+ // 7. rawBytes.length < 32 → PRODUCTION_FACTORY_ENCRYPTION_KEY_TOO_SHORT_001
74
+ // 7.5 keyId not non-empty → PRODUCTION_FACTORY_ENCRYPTION_KEY_ID_INVALID_001
75
+ // 8. DB not structurally current → PRODUCTION_FACTORY_DB_NOT_READY_001
76
+ // 9. missing rawDocumentStore → RAW_DOCUMENT_STORE_FACTORY_COMPATIBLE_REQUIRED_001
77
+ // 9.5 rawDocumentStore wrong marker → RAW_DOCUMENT_STORE_INMEMORY_REJECTED_001
78
+ // 10. missing auditLog → AUDIT_LOG_FACTORY_COMPATIBLE_REQUIRED_001
79
+ // 10.5 auditLog wrong marker → AUDIT_LOG_INMEMORY_OR_NOOP_REJECTED_001
80
+ // 11. construct durable chain + open SQLiteProposalQueue:
81
+ // marker fail → PRODUCTION_FACTORY_PROPOSAL_QUEUE_COMPATIBLE_REQUIRED_001
82
+ // all valid → steps 15–19 (assembly) → return ProductionApiBundle
83
+ //
84
+ // AUTH_FACTORY_AUTH_ADAPTER_OPAQUE_001: this file must not import JwtAuthAdapter,
85
+ // JwtKeyRingAuthAdapter, StaticJwtKeyRing, or jwt-core. The factory is AuthAdapter-opaque.
86
+ // PRODUCTION_FACTORY_DB_PRE_MIGRATED_001: the factory never calls runMigrations; the host
87
+ // must call runMigrations(db) before passing db to this factory.
88
+ // PRODUCTION_FACTORY_NO_SECRET_LOGGING_001: rawBytes is never included in error messages,
89
+ // error properties, log statements, or serialized forms.
90
+ // PRODUCTION_FACTORY_JWT_AND_AT_REST_KEYS_SEPARATE_001: encryptionKey.rawBytes is never
91
+ // passed to any JWT component.
92
+ //
93
+ // Platform-02 (TrustedDataSourceRegistry + ProposalPolicy) codes:
94
+ // PROPOSAL_POLICY_OPTIONAL_NO_CHECK_WHEN_ABSENT_001: neither trustedDataSourceRegistry nor
95
+ // proposalPolicy set → effectiveQueue === queue; behavior is byte-identical to today.
96
+ // PROPOSAL_POLICY_EXPLICIT_WINS_OVER_REGISTRY_001: explicit config.proposalPolicy wins over
97
+ // the trustedDataSourceRegistry convenience wiring when both are provided.
98
+ import { safeLog } from "../observability/logger.js";
99
+ import { safeRecord } from "../observability/metrics.js";
100
+ import { CURRENT_SCHEMA_VERSION, verifyCurrentSchema } from "../persistence/sqlite/migrations.js";
101
+ import { StaticKeyProvider } from "../crypto/static-key.js";
102
+ import { AesGcmCryptoBoundary } from "../crypto/aes-gcm.js";
103
+ import { ClaimSerializer } from "../crypto/claim-serializer.js";
104
+ import { SQLiteFactStore } from "../persistence/sqlite/sqlite-fact-store.js";
105
+ import { SQLiteSourceRegistry } from "../persistence/sqlite/sqlite-source-registry.js";
106
+ import { SQLiteUnitOfWork } from "../persistence/sqlite/sqlite-unit-of-work.js";
107
+ import { SQLiteAuditLog } from "../audit/sqlite-audit-log.js";
108
+ import { SourceAwareFactStore } from "../source/source-aware-fact-store.js";
109
+ import { SourceAwareClaimAppender } from "../proposal/source-aware-claim-appender.js";
110
+ import { GrantRegistry } from "../consent/grant-registry.js";
111
+ import { SQLiteProposalQueue } from "../proposal/sqlite-proposal-queue.js";
112
+ import { EvidenceBackedContextQueryService } from "../surface/evidence-backed-context-query.js";
113
+ import { EvidenceBackedClaimCreator } from "../evidence/evidence-backed-claim-creator.js";
114
+ import { AccessContextIssuer } from "../auth/access-context-issuer.js";
115
+ import { LifeApiImpl } from "./create-life-api.js";
116
+ import { DefaultAuthorizationPolicy } from "../auth/default-authorization-policy.js";
117
+ import { BackendContextMintingBoundary } from "../auth/backend-context-minting-boundary.js";
118
+ import { StaticTrustTable } from "../resolution/trust-profile.js";
119
+ import { ProductionScopedLifeApiImpl } from "../auth/production-scoped-life-api.js";
120
+ import { RegistryBackedProposalPolicy } from "../proposal/proposal-policy.js";
121
+ import { PolicyAwareProposalQueue } from "../proposal/policy-aware-proposal-queue.js";
122
+ export async function createSQLiteLifeApiForProduction(config) {
123
+ // Steps 1–2: authAdapter guard.
124
+ if (config == null || config.authAdapter == null) {
125
+ // If config is non-null, logger/metrics may be present — emit diagnostics before throwing.
126
+ if (config != null) {
127
+ safeLog(config.logger, {
128
+ level: "error", code: "LOG_AUTH_ADAPTER_MISSING_001",
129
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
130
+ message: "factory readiness failed: authAdapter missing",
131
+ timestamp: new Date().toISOString(),
132
+ });
133
+ safeRecord(config.metrics, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
134
+ status: "failed", errorCode: "AUTH_HOST_ADAPTER_REQUIRED_001" });
135
+ }
136
+ throw Object.assign(new Error("AUTH_HOST_ADAPTER_REQUIRED_001: createSQLiteLifeApiForProduction requires " +
137
+ "an AuthAdapter. Provide an implementation that verifies external credentials " +
138
+ "before minting trusted AccessContexts. Without an AuthAdapter, the minting " +
139
+ "boundary cannot enforce authentication — do not omit this argument."), { code: "AUTH_HOST_ADAPTER_REQUIRED_001" });
140
+ }
141
+ // Extract logger and metrics recorder once — available from step 3 onward.
142
+ const logger = config.logger;
143
+ const metricsRecorder = config.metrics;
144
+ // Step 3: tenantBoundary guard.
145
+ if (config.tenantBoundary == null) {
146
+ safeLog(logger, {
147
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
148
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
149
+ message: "factory readiness failed: tenantBoundary missing",
150
+ timestamp: new Date().toISOString(),
151
+ });
152
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
153
+ status: "failed", errorCode: "AUTH_TENANT_BOUNDARY_REQUIRED_001" });
154
+ throw Object.assign(new Error("AUTH_TENANT_BOUNDARY_REQUIRED_001: createSQLiteLifeApiForProduction requires " +
155
+ "a TenantBoundary. Every OwnerContext mint must pass a pre-mint tenant-ownership " +
156
+ "check. Without a TenantBoundary, the factory cannot enforce tenant isolation."), { code: "AUTH_TENANT_BOUNDARY_REQUIRED_001" });
157
+ }
158
+ // Step 4: grantResolver guard.
159
+ if (config.grantResolver == null) {
160
+ safeLog(logger, {
161
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
162
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
163
+ message: "factory readiness failed: grantResolver missing",
164
+ timestamp: new Date().toISOString(),
165
+ });
166
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
167
+ status: "failed", errorCode: "AUTH_GRANT_RESOLVER_REQUIRED_001" });
168
+ throw Object.assign(new Error("AUTH_GRANT_RESOLVER_REQUIRED_001: createSQLiteLifeApiForProduction requires " +
169
+ "a GrantResolver. Every AgentConsentContext mint requires a resolved, unexpired, " +
170
+ "tenant-scoped grant. Without a GrantResolver, the factory cannot enforce agent " +
171
+ "grant scope — do not omit this argument."), { code: "AUTH_GRANT_RESOLVER_REQUIRED_001" });
172
+ }
173
+ // Step 5: db guard (PRODUCTION_FACTORY_DB_REQUIRED_001).
174
+ if (config.db == null) {
175
+ safeLog(logger, {
176
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
177
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
178
+ message: "factory readiness failed: db missing",
179
+ timestamp: new Date().toISOString(),
180
+ });
181
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
182
+ status: "failed", errorCode: "PRODUCTION_FACTORY_DB_REQUIRED_001" });
183
+ throw Object.assign(new Error("PRODUCTION_FACTORY_DB_REQUIRED_001: createSQLiteLifeApiForProduction requires " +
184
+ "a pre-migrated Database instance. Call runMigrations(db) on a better-sqlite3 " +
185
+ "Database before passing it to the factory. The factory does not open database " +
186
+ "files by path and does not run migrations itself."), { code: "PRODUCTION_FACTORY_DB_REQUIRED_001" });
187
+ }
188
+ // Step 6: encryption source — exactly one of keyProvider or encryptionKey must be provided.
189
+ // PRODUCTION_KMS_ADAPTER_AMBIGUOUS_CONFIG_REJECTED_001: both present → fail closed.
190
+ // PRODUCTION_KMS_ADAPTER_FACTORY_ACCEPTS_KEYPROVIDER_001: keyProvider path.
191
+ // PRODUCTION_FACTORY_NO_SECRET_LOGGING_001: no key material in any error or log event.
192
+ const hasKeyProvider = config.keyProvider != null;
193
+ const hasEncryptionKey = config.encryptionKey != null;
194
+ if (hasKeyProvider && hasEncryptionKey) {
195
+ safeLog(logger, {
196
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
197
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
198
+ message: "factory readiness failed: both keyProvider and encryptionKey supplied — provide exactly one",
199
+ timestamp: new Date().toISOString(),
200
+ // No key material, no keyId — PRODUCTION_FACTORY_NO_SECRET_LOGGING_001
201
+ });
202
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
203
+ status: "failed", errorCode: "PRODUCTION_FACTORY_ENCRYPTION_SOURCE_AMBIGUOUS_001" });
204
+ throw Object.assign(new Error("PRODUCTION_FACTORY_ENCRYPTION_SOURCE_AMBIGUOUS_001: " +
205
+ "createSQLiteLifeApiForProduction received both keyProvider and encryptionKey. " +
206
+ "Supply exactly one encryption source: keyProvider (for an externally managed " +
207
+ "KeyProvider) or encryptionKey (for a StaticKeyProvider from raw bytes). " +
208
+ "Providing both is ambiguous and is rejected fail-closed."), { code: "PRODUCTION_FACTORY_ENCRYPTION_SOURCE_AMBIGUOUS_001" });
209
+ }
210
+ // Step 6.5: keyProvider structural validation.
211
+ // PRODUCTION_KMS_ADAPTER_INVALID_KEYPROVIDER_REJECTED_001
212
+ if (hasKeyProvider) {
213
+ const kp = config.keyProvider;
214
+ if (typeof kp !== "object" ||
215
+ typeof kp.getCurrentKey !== "function" ||
216
+ typeof kp.getKey !== "function") {
217
+ safeLog(logger, {
218
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
219
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
220
+ message: "factory readiness failed: keyProvider is not a valid KeyProvider",
221
+ timestamp: new Date().toISOString(),
222
+ // No key material, no keyId — PRODUCTION_FACTORY_NO_SECRET_LOGGING_001
223
+ });
224
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
225
+ status: "failed", errorCode: "PRODUCTION_FACTORY_KEY_PROVIDER_INVALID_001" });
226
+ throw Object.assign(new Error("PRODUCTION_FACTORY_KEY_PROVIDER_INVALID_001: the supplied keyProvider is not a " +
227
+ "valid KeyProvider. A KeyProvider must be a non-null object with getCurrentKey() " +
228
+ "and getKey() methods. Provide an implementation of the KeyProvider interface."), { code: "PRODUCTION_FACTORY_KEY_PROVIDER_INVALID_001" });
229
+ }
230
+ // keyProvider is structurally valid — will be used at assembly time.
231
+ }
232
+ else if (!hasEncryptionKey) {
233
+ // Step 6.6: neither provided — PRODUCTION_FACTORY_ENCRYPTION_KEY_REQUIRED_001.
234
+ safeLog(logger, {
235
+ level: "error", code: "LOG_ENCRYPTION_KEY_INVALID_001",
236
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
237
+ message: "factory readiness failed: neither keyProvider nor encryptionKey provided",
238
+ timestamp: new Date().toISOString(),
239
+ });
240
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
241
+ status: "failed", errorCode: "PRODUCTION_FACTORY_ENCRYPTION_KEY_REQUIRED_001" });
242
+ throw Object.assign(new Error("PRODUCTION_FACTORY_ENCRYPTION_KEY_REQUIRED_001: createSQLiteLifeApiForProduction " +
243
+ "requires an encryption source. Provide either: keyProvider (a KeyProvider " +
244
+ "implementation) or encryptionKey ({ keyId: string, rawBytes: Uint8Array } with " +
245
+ "rawBytes of at least 32 bytes). The factory uses AES-256-GCM encryption at rest."), { code: "PRODUCTION_FACTORY_ENCRYPTION_KEY_REQUIRED_001" });
246
+ }
247
+ else {
248
+ // Step 7: rawBytes length guard (PRODUCTION_FACTORY_ENCRYPTION_KEY_TOO_SHORT_001).
249
+ // PRODUCTION_FACTORY_NO_SECRET_LOGGING_001: rawBytes never included in message or log.
250
+ const encKey = config.encryptionKey;
251
+ if (!(encKey.rawBytes instanceof Uint8Array) ||
252
+ encKey.rawBytes.length < 32) {
253
+ safeLog(logger, {
254
+ level: "error", code: "LOG_ENCRYPTION_KEY_INVALID_001",
255
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
256
+ message: "factory readiness failed: encryptionKey.rawBytes validation failed",
257
+ timestamp: new Date().toISOString(),
258
+ // No rawBytes, no key length, no keyId — PRODUCTION_FACTORY_NO_SECRET_LOGGING_001
259
+ });
260
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
261
+ status: "failed", errorCode: "PRODUCTION_FACTORY_ENCRYPTION_KEY_TOO_SHORT_001" });
262
+ throw Object.assign(new Error("PRODUCTION_FACTORY_ENCRYPTION_KEY_TOO_SHORT_001: encryptionKey.rawBytes must " +
263
+ "be a Uint8Array of at least 32 bytes for AES-256-GCM. Supply a 32-byte or " +
264
+ "longer Uint8Array derived from a cryptographically secure key source."), { code: "PRODUCTION_FACTORY_ENCRYPTION_KEY_TOO_SHORT_001" });
265
+ }
266
+ // Step 7.5: keyId must be a non-empty string (PRODUCTION_FACTORY_ENCRYPTION_KEY_ID_INVALID_001).
267
+ if (typeof encKey.keyId !== "string" || encKey.keyId.length === 0) {
268
+ safeLog(logger, {
269
+ level: "error", code: "LOG_ENCRYPTION_KEY_INVALID_001",
270
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
271
+ message: "factory readiness failed: encryptionKey.keyId validation failed",
272
+ timestamp: new Date().toISOString(),
273
+ // No keyId value emitted — PRODUCTION_FACTORY_NO_SECRET_LOGGING_001
274
+ });
275
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
276
+ status: "failed", errorCode: "PRODUCTION_FACTORY_ENCRYPTION_KEY_ID_INVALID_001" });
277
+ throw Object.assign(new Error("PRODUCTION_FACTORY_ENCRYPTION_KEY_ID_INVALID_001: encryptionKey.keyId must be a " +
278
+ "non-empty string identifying the key version (e.g. 'key-v1', 'prod-2026-01'). " +
279
+ "Provide a stable, non-empty string identifier for the supplied rawBytes material."), { code: "PRODUCTION_FACTORY_ENCRYPTION_KEY_ID_INVALID_001" });
280
+ }
281
+ }
282
+ // Step 8: DB readiness — migration_version assertion + full structural verification.
283
+ // migration_version alone is insufficient: a DB claiming current version but missing
284
+ // auth_tenant_memberships or auth_grants must fail closed.
285
+ // The factory does NOT run migrations — the caller must have called runMigrations(db).
286
+ // PRODUCTION_FACTORY_DB_NOT_READY_001 — PRODUCTION_FACTORY_DB_PRE_MIGRATED_001
287
+ const dbVersionRow = (() => {
288
+ try {
289
+ return config.db
290
+ .prepare("SELECT value FROM schema_meta WHERE key = 'migration_version'")
291
+ .get();
292
+ }
293
+ catch {
294
+ return undefined;
295
+ }
296
+ })();
297
+ const dbVersion = dbVersionRow !== undefined ? parseInt(dbVersionRow.value, 10) : NaN;
298
+ if (!Number.isFinite(dbVersion) || dbVersion !== CURRENT_SCHEMA_VERSION) {
299
+ const found = Number.isFinite(dbVersion) ? String(dbVersion) : "(absent)";
300
+ safeLog(logger, {
301
+ level: "error", code: "LOG_SCHEMA_NOT_READY_001",
302
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
303
+ message: "factory readiness failed: schema version mismatch",
304
+ timestamp: new Date().toISOString(),
305
+ });
306
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
307
+ status: "failed", errorCode: "PRODUCTION_FACTORY_DB_NOT_READY_001" });
308
+ throw Object.assign(new Error(`PRODUCTION_FACTORY_DB_NOT_READY_001: The supplied database has ` +
309
+ `migration_version ${found}, but this factory requires ` +
310
+ `migration_version ${CURRENT_SCHEMA_VERSION}. ` +
311
+ `Call runMigrations(db) before passing the database to the production factory.`), { code: "PRODUCTION_FACTORY_DB_NOT_READY_001", found, required: CURRENT_SCHEMA_VERSION });
312
+ }
313
+ // Full structural verification — migration_version alone is insufficient.
314
+ // A DB claiming migration_version 6 but missing auth_tenant_memberships or auth_grants
315
+ // must fail closed (SQLITE_MIGRATION_CURRENT_SCHEMA_STRUCTURAL_VERIFY_001).
316
+ const structuralCheck = verifyCurrentSchema(config.db);
317
+ if (!structuralCheck.ok) {
318
+ safeLog(logger, {
319
+ level: "error", code: "LOG_SCHEMA_NOT_READY_001",
320
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
321
+ message: "factory readiness failed: schema structural verification failed",
322
+ timestamp: new Date().toISOString(),
323
+ });
324
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
325
+ status: "failed", errorCode: "PRODUCTION_FACTORY_DB_NOT_READY_001" });
326
+ throw Object.assign(new Error(`PRODUCTION_FACTORY_DB_NOT_READY_001: The supplied database claims ` +
327
+ `migration_version ${CURRENT_SCHEMA_VERSION} but fails structural verification. ` +
328
+ `Missing tables or columns: ${structuralCheck.missing.join(", ")}. ` +
329
+ `Call runMigrations(db) before passing the database to the production factory.`), {
330
+ code: "PRODUCTION_FACTORY_DB_NOT_READY_001",
331
+ found: String(CURRENT_SCHEMA_VERSION),
332
+ required: CURRENT_SCHEMA_VERSION,
333
+ missing: [...structuralCheck.missing],
334
+ });
335
+ }
336
+ // Step 9: rawDocumentStore must be present and factory-compatible.
337
+ // RAW_DOCUMENT_STORE_FACTORY_COMPATIBLE_REQUIRED_001
338
+ if (config.rawDocumentStore == null) {
339
+ safeLog(logger, {
340
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
341
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
342
+ message: "factory readiness failed: rawDocumentStore missing",
343
+ timestamp: new Date().toISOString(),
344
+ });
345
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
346
+ status: "failed", errorCode: "RAW_DOCUMENT_STORE_FACTORY_COMPATIBLE_REQUIRED_001" });
347
+ throw Object.assign(new Error("RAW_DOCUMENT_STORE_FACTORY_COMPATIBLE_REQUIRED_001: createSQLiteLifeApiForProduction " +
348
+ "requires a FactoryCompatibleRawDocumentStore. Provide a SQLiteRawDocumentStore or a " +
349
+ "custom implementation that declares factoryCompatible: \"durable-raw-document-store\". " +
350
+ "InMemoryRawDocumentStore and test-only implementations are not accepted."), { code: "RAW_DOCUMENT_STORE_FACTORY_COMPATIBLE_REQUIRED_001" });
351
+ }
352
+ // Step 9.5: validate the durable marker — defense-in-depth against TypeScript casts.
353
+ // RAW_DOCUMENT_STORE_INMEMORY_REJECTED_001
354
+ if (config.rawDocumentStore.factoryCompatible !==
355
+ "durable-raw-document-store") {
356
+ safeLog(logger, {
357
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
358
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
359
+ message: "factory readiness failed: rawDocumentStore not durable-compatible",
360
+ timestamp: new Date().toISOString(),
361
+ });
362
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
363
+ status: "failed", errorCode: "RAW_DOCUMENT_STORE_INMEMORY_REJECTED_001" });
364
+ throw Object.assign(new Error("RAW_DOCUMENT_STORE_INMEMORY_REJECTED_001: the supplied rawDocumentStore does not carry " +
365
+ "the factory-compatible durable marker (factoryCompatible: \"durable-raw-document-store\"). " +
366
+ "InMemoryRawDocumentStore, noop, and test-only implementations are not accepted. " +
367
+ "Supply a SQLiteRawDocumentStore or a custom durable implementation with the marker."), { code: "RAW_DOCUMENT_STORE_INMEMORY_REJECTED_001" });
368
+ }
369
+ // Step 10: auditLog must be present and factory-compatible.
370
+ // AUDIT_LOG_FACTORY_COMPATIBLE_REQUIRED_001
371
+ if (config.auditLog == null) {
372
+ safeLog(logger, {
373
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
374
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
375
+ message: "factory readiness failed: auditLog missing",
376
+ timestamp: new Date().toISOString(),
377
+ });
378
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
379
+ status: "failed", errorCode: "AUDIT_LOG_FACTORY_COMPATIBLE_REQUIRED_001" });
380
+ throw Object.assign(new Error("AUDIT_LOG_FACTORY_COMPATIBLE_REQUIRED_001: createSQLiteLifeApiForProduction " +
381
+ "requires a FactoryCompatibleAuditLog. Provide a SQLiteAuditLog or a custom " +
382
+ "implementation that declares factoryCompatible: \"durable-audit-log\". " +
383
+ "InMemoryAuditLog, noop audit sinks, and test-only implementations are not accepted."), { code: "AUDIT_LOG_FACTORY_COMPATIBLE_REQUIRED_001" });
384
+ }
385
+ // Step 10.5: validate the durable marker — defense-in-depth against TypeScript casts.
386
+ // AUDIT_LOG_INMEMORY_OR_NOOP_REJECTED_001
387
+ if (config.auditLog.factoryCompatible !==
388
+ "durable-audit-log") {
389
+ safeLog(logger, {
390
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
391
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
392
+ message: "factory readiness failed: auditLog not durable-compatible",
393
+ timestamp: new Date().toISOString(),
394
+ });
395
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
396
+ status: "failed", errorCode: "AUDIT_LOG_INMEMORY_OR_NOOP_REJECTED_001" });
397
+ throw Object.assign(new Error("AUDIT_LOG_INMEMORY_OR_NOOP_REJECTED_001: the supplied auditLog does not carry " +
398
+ "the factory-compatible durable marker (factoryCompatible: \"durable-audit-log\"). " +
399
+ "InMemoryAuditLog, noop audit sinks, and test-only implementations are not accepted. " +
400
+ "Supply a SQLiteAuditLog or a custom durable implementation with the marker."), { code: "AUDIT_LOG_INMEMORY_OR_NOOP_REJECTED_001" });
401
+ }
402
+ // Step 11: durable internal ProposalQueue construction proof (Production-06d-c-a).
403
+ //
404
+ // PRODUCTION_FACTORY_ASYNC_DEFERRED_BOUNDARY_001
405
+ // PRODUCTION_FACTORY_PROPOSAL_QUEUE_INTERNAL_CONSTRUCTION_REVIEWED_001
406
+ // PROPOSAL_QUEUE_DURABLE_APPENDER_REQUIRED_001
407
+ // PROPOSAL_QUEUE_NO_INMEMORY_FACTSTORE_APPENDER_001
408
+ // PROPOSAL_QUEUE_NO_NOOP_UNIT_OF_WORK_ASSEMBLY_001
409
+ //
410
+ // All core deps valid (steps 1–10.5). Resolve encryption source and construct crypto boundary.
411
+ // PRODUCTION_FACTORY_NO_SECRET_LOGGING_001: rawBytes is never included in any error.
412
+ // PRODUCTION_FACTORY_JWT_AND_AT_REST_KEYS_SEPARATE_001: resolvedKeyProvider never passed to JWT.
413
+ // PRODUCTION_KMS_ADAPTER_FACTORY_ACCEPTS_KEYPROVIDER_001: external keyProvider used directly.
414
+ // PRODUCTION_KMS_ADAPTER_STATIC_KEY_PATH_UNCHANGED_001: encryptionKey path unchanged.
415
+ let resolvedKeyProvider;
416
+ if (config.keyProvider != null) {
417
+ resolvedKeyProvider = config.keyProvider;
418
+ safeLog(logger, {
419
+ level: "info", code: "LOG_KEY_PROVIDER_WIRED_001",
420
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
421
+ message: "external keyProvider wired — encryptionKey path skipped",
422
+ timestamp: new Date().toISOString(),
423
+ // No key material, no keyId — PRODUCTION_FACTORY_NO_SECRET_LOGGING_001
424
+ });
425
+ }
426
+ else {
427
+ // config.encryptionKey is guaranteed non-null here: validated at step 7/7.5.
428
+ const ek = config.encryptionKey;
429
+ resolvedKeyProvider = await StaticKeyProvider.fromRawBytes(ek.keyId, ek.rawBytes);
430
+ }
431
+ const crypto = new AesGcmCryptoBoundary(resolvedKeyProvider);
432
+ const claimSerializer = new ClaimSerializer(crypto);
433
+ const sqliteFactStore = new SQLiteFactStore(config.db, claimSerializer);
434
+ const sqliteSourceReg = new SQLiteSourceRegistry(config.db);
435
+ const sourceAwareStore = new SourceAwareFactStore(sqliteFactStore, sqliteSourceReg);
436
+ const sqliteUoW = new SQLiteUnitOfWork(config.db);
437
+ const appender = new SourceAwareClaimAppender(sourceAwareStore, sqliteSourceReg, sqliteUoW);
438
+ // C2a: internal audit log for allow-path mutation audit — shares config.db connection.
439
+ // PRODUCTION_AUDIT_INTERNAL_LOG_SHARED_CONNECTION_001: audit_events table guaranteed by
440
+ // schema migration (schema v5+). Passing the Database instance sets _ownsConnection = false,
441
+ // so dispose does not close config.db. Distinct from the caller-supplied config.auditLog.
442
+ const internalMutationAuditLog = new SQLiteAuditLog(config.db);
443
+ // Internal GrantRegistry — in-memory, zero deps; distinct from config.grantResolver.
444
+ const grantRegistry = new GrantRegistry();
445
+ // Open SQLiteProposalQueue with the full durable appender chain.
446
+ // sqliteFactStore, sqliteSourceReg, and sqliteUoW all share config.db — single transaction
447
+ // scope (UNIT_OF_WORK_SQLITE_TRANSACTION_001).
448
+ const queue = await SQLiteProposalQueue.open(config.db, crypto, appender, grantRegistry, sqliteSourceReg);
449
+ // PRODUCTION_FACTORY_PROPOSAL_QUEUE_COMPATIBLE_REQUIRED_001
450
+ // Runtime two-layer defense: TypeScript structural check + runtime marker assertion.
451
+ if (queue.factoryCompatible !==
452
+ "durable-proposal-queue") {
453
+ safeLog(logger, {
454
+ level: "error", code: "LOG_FACTORY_READINESS_FAILED_001",
455
+ component: "SQLiteLifeApiFactory", operation: "createSQLiteLifeApiForProduction",
456
+ message: "factory readiness failed: proposalQueue not durable-compatible",
457
+ timestamp: new Date().toISOString(),
458
+ });
459
+ safeRecord(metricsRecorder, "factory.create.failure", 1, { component: "factory", operation: "createSQLiteLifeApiForProduction",
460
+ status: "failed", errorCode: "PRODUCTION_FACTORY_PROPOSAL_QUEUE_COMPATIBLE_REQUIRED_001" });
461
+ throw Object.assign(new Error("PRODUCTION_FACTORY_PROPOSAL_QUEUE_COMPATIBLE_REQUIRED_001: the constructed " +
462
+ "ProposalQueue does not carry the expected factory-compatible marker " +
463
+ "(factoryCompatible: \"durable-proposal-queue\"). SQLiteProposalQueue must declare " +
464
+ "this marker via FactoryCompatibleProposalQueue."), { code: "PRODUCTION_FACTORY_PROPOSAL_QUEUE_COMPATIBLE_REQUIRED_001" });
465
+ }
466
+ // Platform-02: resolve ProposalPolicy wiring. Explicit config.proposalPolicy wins over
467
+ // the trustedDataSourceRegistry convenience wiring (PROPOSAL_POLICY_EXPLICIT_WINS_OVER_REGISTRY_001).
468
+ // Neither field set → resolvedPolicy is undefined → effectiveQueue === queue, byte-identical
469
+ // to pre-Platform-02 behavior (PROPOSAL_POLICY_OPTIONAL_NO_CHECK_WHEN_ABSENT_001).
470
+ const resolvedPolicy = config.proposalPolicy ??
471
+ (config.trustedDataSourceRegistry != null
472
+ ? new RegistryBackedProposalPolicy(config.trustedDataSourceRegistry)
473
+ : undefined);
474
+ const effectiveQueue = resolvedPolicy !== undefined
475
+ ? new PolicyAwareProposalQueue(queue, resolvedPolicy, sqliteSourceReg, internalMutationAuditLog)
476
+ : queue;
477
+ // PRODUCTION_FACTORY_BLOCKER_LIST_EMPTY_REVIEWED_001
478
+ // All durable dependency blockers resolved. Proceeding to final assembly.
479
+ //
480
+ // Step 15: clock — config.clock is optional; fall back to system wall clock.
481
+ // PRODUCTION_FACTORY_CLOCK_FALLBACK_SAFE_001
482
+ const clock = config.clock ?? { now: () => new Date().toISOString() };
483
+ // Step 16: EvidenceBackedContextQueryService.
484
+ // PRODUCTION_FACTORY_QUERY_SVC_EMPTY_TRUST_001: StaticTrustTable.empty() — no
485
+ // cross-source trust chains at factory level.
486
+ const querySvc = new EvidenceBackedContextQueryService(sourceAwareStore, // FactStore
487
+ grantRegistry, // GrantRegistry
488
+ config.auditLog, // AuditLog
489
+ sqliteSourceReg, // SourceRegistry
490
+ config.rawDocumentStore, // RawDocumentStore
491
+ StaticTrustTable.empty());
492
+ // Step 17: EvidenceBackedClaimCreator.
493
+ // PRODUCTION_FACTORY_UOW_SHARED_001: sqliteUoW is shared with appender — claim writes
494
+ // and proposal-accept writes share a single SQLite transaction scope.
495
+ const claimCreator = new EvidenceBackedClaimCreator(config.rawDocumentStore, // RawDocumentStore
496
+ sqliteSourceReg, // SourceRegistry
497
+ sourceAwareStore, // FactStore
498
+ sqliteUoW);
499
+ // Step 18: co-create AccessContextIssuer and LifeApiImpl.
500
+ // PRODUCTION_FACTORY_ISSUER_API_PAIRING_001: issuer and rawApi are created together in
501
+ // this factory call. The issuer's WeakSet is private to this invocation — contexts minted
502
+ // by this bundle's auth are accepted by rawApi; all others are rejected.
503
+ // PRODUCTION_FACTORY_RAW_API_NOT_RETURNED_001: rawApi is captured in createScopedApi
504
+ // closure only; it is never returned as a public value.
505
+ const issuer = new AccessContextIssuer();
506
+ const rawApi = new LifeApiImpl(querySvc, // EvidenceBackedContextQueryService
507
+ claimCreator, // EvidenceBackedClaimCreator
508
+ effectiveQueue, // ProposalQueue (SQLiteProposalQueue, optionally policy-wrapped — Platform-02)
509
+ config.rawDocumentStore, // RawDocumentStore
510
+ sqliteSourceReg, // SourceRegistry
511
+ grantRegistry, // GrantRegistry
512
+ issuer, // AccessContextIssuer — paired with BackendContextMintingBoundary below
513
+ sourceAwareStore, // FactStore
514
+ config.auditLog, // AuditLog — general/deny/query audit (caller-supplied)
515
+ internalMutationAuditLog);
516
+ // Step 19: DefaultAuthorizationPolicy + BackendContextMintingBoundary + bundle.
517
+ // BackendContextMintingBoundary is a factory-internal production dependency here.
518
+ // Not exported from src/index.ts; callers receive ContextMintingBoundary only.
519
+ const policy = new DefaultAuthorizationPolicy(clock);
520
+ const auth = new BackendContextMintingBoundary({
521
+ adapter: config.authAdapter,
522
+ policy,
523
+ issuer, // SAME AccessContextIssuer as rawApi — paired
524
+ clock,
525
+ tenantBoundary: config.tenantBoundary,
526
+ grantResolver: config.grantResolver,
527
+ ...(config.trustedAgentRegistry != null
528
+ ? { trustedAgentRegistry: config.trustedAgentRegistry }
529
+ : {}),
530
+ // Platform-03: guardianRegistry optional; absent = mintGuardianContext throws GUARDIAN_REGISTRY_NOT_CONFIGURED_001.
531
+ // GUARDIAN_REGISTRY_OPTIONAL_NO_CHECK_WHEN_ABSENT_001
532
+ ...(config.guardianRegistry != null
533
+ ? { guardianRegistry: config.guardianRegistry, guardianAuditLog: internalMutationAuditLog }
534
+ : {}),
535
+ });
536
+ // PRODUCTION_FACTORY_SCOPED_API_TRUST_GATED_001: createScopedApi calls
537
+ // issuer.assertTrusted(mintedCtx.context) before constructing ProductionScopedLifeApiImpl.
538
+ // Trust is enforced at capability creation — not deferred to the first method call.
539
+ // Rejects: manually constructed AccessContext objects; contexts from a different factory
540
+ // invocation's issuer (cross-bundle replay). Accepts: contexts minted by this bundle's auth.
541
+ // PRODUCTION_FACTORY_BUNDLE_COMPLETE_001
542
+ const createScopedApi = (mintedCtx) => {
543
+ issuer.assertTrusted(mintedCtx.context);
544
+ return new ProductionScopedLifeApiImpl(rawApi, mintedCtx, config.auditLog, internalMutationAuditLog, sqliteUoW, logger, metricsRecorder);
545
+ };
546
+ // Step 20: healthCheck closure — D2b local readiness diagnostic.
547
+ //
548
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_SURFACE_SPECIFIED_001
549
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_NO_DATA_EXPOSURE_001: no DB path, SQL values,
550
+ // keyId, rawBytes, user data, Error.message, or Error.stack in result or log events.
551
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_NO_AUDIT_SIDE_EFFECT_001: no audit writes.
552
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_LOGGER_NON_INTERFERING_001: logger failure is
553
+ // swallowed (safeLog); result is valid regardless of logger state.
554
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_DOES_NOT_THROW_001: ordinary check failures
555
+ // return { status: "failed" } — never throw to the caller.
556
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_NO_CONSOLE_001: output only via safeLog.
557
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_ITEM_NAMES_STABLE_001: seven stable names.
558
+ const healthCheck = async () => {
559
+ const checkedAt = new Date().toISOString();
560
+ const items = [];
561
+ // Inner helper: run one synchronous check; catch failures; emit a sanitized log event.
562
+ // Never throws — always returns a HealthCheckItem.
563
+ const runCheck = (name, okCode, failCode, fn) => {
564
+ try {
565
+ fn();
566
+ safeRecord(metricsRecorder, "health_check.outcome", 1, { component: "healthCheck", operation: "healthCheck", checkName: name, status: "ok" });
567
+ return { name, status: "ok", code: okCode };
568
+ }
569
+ catch (err) {
570
+ safeLog(logger, {
571
+ level: "warn",
572
+ code: "LOG_HEALTHCHECK_CHECK_FAILED_001",
573
+ component: "ProductionApiBundle",
574
+ operation: "healthCheck",
575
+ message: "health check failed: " + name,
576
+ timestamp: new Date().toISOString(),
577
+ // No error message or stack — errorName only.
578
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_NO_DATA_EXPOSURE_001
579
+ ...(err instanceof Error ? { errorName: err.name } : {}),
580
+ });
581
+ safeRecord(metricsRecorder, "health_check.outcome", 1, { component: "healthCheck", operation: "healthCheck", checkName: name, status: "failed" });
582
+ return {
583
+ name,
584
+ status: "failed",
585
+ code: failCode,
586
+ // errorName only — no message, no stack, no sensitive data.
587
+ ...(err instanceof Error ? { errorName: err.name } : {}),
588
+ };
589
+ }
590
+ };
591
+ // 1. db-connectivity — SELECT 1; no DML, no DDL, no user data.
592
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_DB_CHECK_STATELESS_001
593
+ items.push(runCheck("db-connectivity", "HEALTHCHECK_DB_CONNECTIVITY_OK_001", "HEALTHCHECK_DB_CONNECTIVITY_FAILED_001", () => { config.db.prepare("SELECT 1").get(); }));
594
+ // 2. schema-version — migration_version === CURRENT_SCHEMA_VERSION.
595
+ items.push(runCheck("schema-version", "HEALTHCHECK_SCHEMA_VERSION_OK_001", "HEALTHCHECK_SCHEMA_VERSION_FAILED_001", () => {
596
+ const row = config.db
597
+ .prepare("SELECT value FROM schema_meta WHERE key = 'migration_version'")
598
+ .get();
599
+ const v = row !== undefined ? parseInt(row.value, 10) : NaN;
600
+ if (!Number.isFinite(v) || v !== CURRENT_SCHEMA_VERSION) {
601
+ throw new Error("HEALTHCHECK_SCHEMA_VERSION_FAILED_001");
602
+ }
603
+ }));
604
+ // 3. schema-structural — verifyCurrentSchema reused; no new verification logic.
605
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_SCHEMA_USES_EXISTING_VERIFY_001
606
+ items.push(runCheck("schema-structural", "HEALTHCHECK_SCHEMA_STRUCTURAL_OK_001", "HEALTHCHECK_SCHEMA_STRUCTURAL_FAILED_001", () => {
607
+ const structResult = verifyCurrentSchema(config.db);
608
+ if (!structResult.ok) {
609
+ // Do not include missing table names in the item — use a stable code only.
610
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_NO_DATA_EXPOSURE_001
611
+ throw new Error("HEALTHCHECK_SCHEMA_STRUCTURAL_FAILED_001");
612
+ }
613
+ }));
614
+ // 4. key-provider-loaded — resolved KeyProvider is non-null after factory assembly.
615
+ // Works for both StaticKeyProvider (encryptionKey path) and external keyProvider.
616
+ items.push(runCheck("key-provider-loaded", "HEALTHCHECK_KEY_PROVIDER_LOADED_OK_001", "HEALTHCHECK_KEY_PROVIDER_LOADED_FAILED_001", () => {
617
+ if (resolvedKeyProvider == null) {
618
+ throw new Error("HEALTHCHECK_KEY_PROVIDER_LOADED_FAILED_001");
619
+ }
620
+ }));
621
+ // 5–7. Opaque adapter reference checks — non-null only; no functional probe.
622
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_OPAQUE_ADAPTERS_STRUCTURAL_ONLY_001
623
+ items.push(runCheck("auth-adapter-present", "HEALTHCHECK_AUTH_ADAPTER_PRESENT_OK_001", "HEALTHCHECK_AUTH_ADAPTER_PRESENT_FAILED_001", () => {
624
+ if (config.authAdapter == null) {
625
+ throw new Error("HEALTHCHECK_AUTH_ADAPTER_PRESENT_FAILED_001");
626
+ }
627
+ }));
628
+ items.push(runCheck("tenant-boundary-present", "HEALTHCHECK_TENANT_BOUNDARY_PRESENT_OK_001", "HEALTHCHECK_TENANT_BOUNDARY_PRESENT_FAILED_001", () => {
629
+ if (config.tenantBoundary == null) {
630
+ throw new Error("HEALTHCHECK_TENANT_BOUNDARY_PRESENT_FAILED_001");
631
+ }
632
+ }));
633
+ items.push(runCheck("grant-resolver-present", "HEALTHCHECK_GRANT_RESOLVER_PRESENT_OK_001", "HEALTHCHECK_GRANT_RESOLVER_PRESENT_FAILED_001", () => {
634
+ if (config.grantResolver == null) {
635
+ throw new Error("HEALTHCHECK_GRANT_RESOLVER_PRESENT_FAILED_001");
636
+ }
637
+ }));
638
+ // Aggregate status — worst-case of all items.
639
+ // PRODUCTION_OBSERVABILITY_HEALTHCHECK_STATUS_AGGREGATE_001
640
+ let status = "ok";
641
+ for (const item of items) {
642
+ if (item.status === "failed") {
643
+ status = "failed";
644
+ break;
645
+ }
646
+ if (item.status === "degraded") {
647
+ status = "degraded";
648
+ }
649
+ }
650
+ return { status, checkedAt, checks: items };
651
+ };
652
+ return { auth, createScopedApi, healthCheck };
653
+ }
654
+ //# sourceMappingURL=create-sqlite-life-api.js.map