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,226 @@
1
+ // src/auth/jwt-core.ts
2
+ // INTERNAL — not exported from src/index.ts.
3
+ // Shared JWT verification primitives used by JwtAuthAdapter and JwtKeyRingAuthAdapter.
4
+ //
5
+ // Credential-verification crypto boundary: this is the only src/auth/** file that may
6
+ // import node:crypto. See CRYPTO_BOUNDARY_ONLY_001 in docs/INVARIANTS.md.
7
+ // jwt-auth-adapter.ts and jwt-key-ring-auth-adapter.ts both delegate here; they do not
8
+ // import node:crypto directly.
9
+ import { createHmac, timingSafeEqual, createPublicKey, verify as cryptoVerify } from "node:crypto";
10
+ // ─── Constants ────────────────────────────────────────────────────────────────
11
+ export const MIN_SECRET_BYTES = 32;
12
+ export const VALID_PRINCIPAL_TYPES = new Set(["user", "agent", "service", "audit"]);
13
+ export const VALID_ROLES = new Set(["owner", "agent", "system", "audit"]);
14
+ const B64URL_PATTERN = /^[A-Za-z0-9\-_]+$/;
15
+ // ─── Error factory ────────────────────────────────────────────────────────────
16
+ export function throwErr(code, detail) {
17
+ throw Object.assign(new Error(`${code}: ${detail}`), { code });
18
+ }
19
+ // ─── Segment helpers ──────────────────────────────────────────────────────────
20
+ export function decodeB64url(segment) {
21
+ if (!B64URL_PATTERN.test(segment)) {
22
+ throwErr("AUTH_CREDENTIAL_MALFORMED_001", "segment contains invalid base64url characters");
23
+ }
24
+ return Buffer.from(segment, "base64url");
25
+ }
26
+ export function parseJsonObject(segment, label) {
27
+ let bytes;
28
+ try {
29
+ bytes = decodeB64url(segment);
30
+ }
31
+ catch {
32
+ throwErr("AUTH_CREDENTIAL_MALFORMED_001", `${label} base64url decode failed`);
33
+ }
34
+ let parsed;
35
+ try {
36
+ parsed = JSON.parse(bytes.toString("utf8"));
37
+ }
38
+ catch {
39
+ throwErr("AUTH_CREDENTIAL_MALFORMED_001", `${label} is not valid JSON`);
40
+ }
41
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
42
+ throwErr("AUTH_CREDENTIAL_MALFORMED_001", `${label} is not a JSON object`);
43
+ }
44
+ return parsed;
45
+ }
46
+ export function splitJwt(credential) {
47
+ const parts = credential.split(".");
48
+ if (parts.length !== 3 || parts.some(s => s.length === 0)) {
49
+ throwErr("AUTH_CREDENTIAL_MALFORMED_001", "token must have exactly 3 non-empty segments");
50
+ }
51
+ return parts;
52
+ }
53
+ export function assertAlgHS256(h) {
54
+ if (h["alg"] !== "HS256") {
55
+ throwErr("AUTH_CREDENTIAL_ALGORITHM_UNSUPPORTED_001", "only HS256 is supported");
56
+ }
57
+ }
58
+ // ─── HMAC-SHA256 with constant-time comparison ────────────────────────────────
59
+ export function verifyHmacSig(rawHeader, rawPayload, rawSig, secret) {
60
+ // Compute expected over the raw (encoded, not decoded) signing input
61
+ const expected = createHmac("sha256", secret).update(rawHeader + "." + rawPayload).digest();
62
+ // Strict base64url before decode to avoid lenient Buffer.from behaviour
63
+ if (!B64URL_PATTERN.test(rawSig)) {
64
+ throwErr("AUTH_CREDENTIAL_MALFORMED_001", "signature has invalid base64url characters");
65
+ }
66
+ const received = Buffer.from(rawSig, "base64url");
67
+ // Length check before timingSafeEqual (length mismatch would throw inside TSE)
68
+ if (received.length !== expected.length || !timingSafeEqual(expected, received)) {
69
+ throwErr("AUTH_CREDENTIAL_SIGNATURE_INVALID_001", "signature verification failed");
70
+ }
71
+ }
72
+ // ─── Claim helpers ────────────────────────────────────────────────────────────
73
+ export function requireStr(p, name) {
74
+ if (!(name in p))
75
+ throwErr("AUTH_CREDENTIAL_CLAIM_MISSING_001", `claim "${name}" is required`);
76
+ if (typeof p[name] !== "string")
77
+ throwErr("AUTH_CREDENTIAL_CLAIM_INVALID_001", `claim "${name}" must be a string`);
78
+ return p[name];
79
+ }
80
+ export function requireStrArr(p, name, valid) {
81
+ if (!(name in p))
82
+ throwErr("AUTH_CREDENTIAL_CLAIM_MISSING_001", `claim "${name}" is required`);
83
+ const val = p[name];
84
+ if (!Array.isArray(val))
85
+ throwErr("AUTH_CREDENTIAL_CLAIM_INVALID_001", `claim "${name}" must be an array`);
86
+ for (const item of val) {
87
+ if (typeof item !== "string") {
88
+ throwErr("AUTH_CREDENTIAL_CLAIM_INVALID_001", `claim "${name}" elements must be strings`);
89
+ }
90
+ if (valid !== null && !valid.has(item)) {
91
+ throwErr("AUTH_CREDENTIAL_CLAIM_INVALID_001", `claim "${name}" contains an unrecognised value`);
92
+ }
93
+ }
94
+ return val;
95
+ }
96
+ // ─── Payload verification + VerifiedPrincipal assembly (steps 11-21) ──────────
97
+ export function verifyPayloadAndBuildPrincipal(p, nowMs, expectedIssuer, expectedAudience) {
98
+ // Step 11 — exp type guard
99
+ const expVal = p["exp"];
100
+ if (typeof expVal !== "number") {
101
+ throwErr("AUTH_CREDENTIAL_TIME_CLAIM_INVALID_001", "exp claim is absent or not a number");
102
+ }
103
+ // Step 12 — exp value guard (leeway = 0, fail-closed at boundary)
104
+ if (expVal * 1000 <= nowMs) {
105
+ throwErr("AUTH_CREDENTIAL_EXPIRED_001", "token has expired");
106
+ }
107
+ // Steps 13-14 — nbf (optional)
108
+ if ("nbf" in p) {
109
+ const nbfVal = p["nbf"];
110
+ if (typeof nbfVal !== "number") {
111
+ throwErr("AUTH_CREDENTIAL_TIME_CLAIM_INVALID_001", "nbf claim is not a number");
112
+ }
113
+ if (nbfVal * 1000 > nowMs) {
114
+ throwErr("AUTH_CREDENTIAL_NOT_YET_VALID_001", "token is not yet valid");
115
+ }
116
+ }
117
+ // Step 15 — iss: absent or mismatch → same error code
118
+ if (p["iss"] !== expectedIssuer) {
119
+ throwErr("AUTH_CREDENTIAL_ISSUER_INVALID_001", "iss claim does not match expected issuer");
120
+ }
121
+ // Steps 16-19 — aud: absent, wrong type, or expected audience not present
122
+ const aud = p["aud"];
123
+ if (typeof aud === "string") {
124
+ if (aud !== expectedAudience) {
125
+ throwErr("AUTH_CREDENTIAL_AUDIENCE_INVALID_001", "aud does not match expected audience");
126
+ }
127
+ }
128
+ else if (Array.isArray(aud)) {
129
+ if (!aud.some(a => a === expectedAudience)) {
130
+ throwErr("AUTH_CREDENTIAL_AUDIENCE_INVALID_001", "aud array does not contain expected audience");
131
+ }
132
+ }
133
+ else {
134
+ throwErr("AUTH_CREDENTIAL_AUDIENCE_INVALID_001", "aud claim is absent or has unsupported type");
135
+ }
136
+ // Step 20 — required claims (fail closed on missing or wrong type/value)
137
+ const sub = requireStr(p, "sub");
138
+ const principalType = requireStr(p, "principalType");
139
+ if (!VALID_PRINCIPAL_TYPES.has(principalType)) {
140
+ throwErr("AUTH_CREDENTIAL_CLAIM_INVALID_001", "principalType is not a recognised value");
141
+ }
142
+ const tenantId = requireStr(p, "tenantId");
143
+ const roles = requireStrArr(p, "roles", VALID_ROLES);
144
+ const grantIds = requireStrArr(p, "grantIds", null);
145
+ // iat — required; CLAIM_MISSING/CLAIM_INVALID (not a time guard)
146
+ if (!("iat" in p)) {
147
+ throwErr("AUTH_CREDENTIAL_CLAIM_MISSING_001", "iat claim is required");
148
+ }
149
+ const iatVal = p["iat"];
150
+ if (typeof iatVal !== "number") {
151
+ throwErr("AUTH_CREDENTIAL_CLAIM_INVALID_001", "iat claim must be a number");
152
+ }
153
+ // Step 21 — assemble VerifiedPrincipal
154
+ const expiresAt = new Date(expVal * 1000).toISOString();
155
+ const issuedAt = new Date(iatVal * 1000).toISOString();
156
+ const base = {
157
+ principalId: sub,
158
+ principalType: principalType,
159
+ tenantId,
160
+ roles: roles,
161
+ grantIds,
162
+ expiresAt,
163
+ issuedAt,
164
+ };
165
+ // authorizedPersonId is optional — must be omitted (not set to undefined)
166
+ const authPersonId = p["authorizedPersonId"];
167
+ if (authPersonId !== undefined) {
168
+ if (typeof authPersonId !== "string") {
169
+ throwErr("AUTH_CREDENTIAL_CLAIM_INVALID_001", "authorizedPersonId must be a string");
170
+ }
171
+ return { ...base, authorizedPersonId: authPersonId };
172
+ }
173
+ return base;
174
+ }
175
+ // ─── RS256 — External IdP crypto boundary ────────────────────────────────────
176
+ //
177
+ // CRYPTO_BOUNDARY_ONLY_001: jwt-core.ts is the sole src/auth/ file that may import
178
+ // node:crypto at runtime. All RS256/RSA operations must live here.
179
+ /**
180
+ * Import an RSA public key from a JWK entry (Record<string, unknown>).
181
+ * Enforces: kty must be "RSA", minimum 2048-bit modulus.
182
+ * Throws EXTERNAL_IDP_PUBLIC_KEY_TOO_SMALL_001 for weak keys.
183
+ * Throws EXTERNAL_IDP_JWKS_PARSE_ERROR_001 for malformed entries.
184
+ * Called only from JwksKeyRing — never from the auth adapter directly.
185
+ */
186
+ export function importRsaPublicKey(entry) {
187
+ let keyObj;
188
+ try {
189
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
+ keyObj = createPublicKey({ key: entry, format: "jwk" });
191
+ }
192
+ catch {
193
+ throwErr("EXTERNAL_IDP_JWKS_PARSE_ERROR_001", "RSA public key import failed");
194
+ }
195
+ const details = keyObj.asymmetricKeyDetails;
196
+ if (details?.modulusLength !== undefined && details.modulusLength < 2048) {
197
+ throwErr("EXTERNAL_IDP_PUBLIC_KEY_TOO_SMALL_001", "RSA key must be at least 2048 bits");
198
+ }
199
+ return keyObj;
200
+ }
201
+ /**
202
+ * Verify an RS256 (PKCS#1 v1.5 / SHA-256) JWT signature.
203
+ * rawSig: base64url-encoded signature (JWT third segment).
204
+ * headerDotPayload: raw signing input ("header.payload", as sent over wire).
205
+ * publicKey: RSA KeyObject returned by importRsaPublicKey.
206
+ * Throws EXTERNAL_IDP_SIGNATURE_INVALID_001 on any verification failure.
207
+ * No sensitive data in error messages.
208
+ */
209
+ export function verifyRsaSig(rawSig, headerDotPayload, publicKey) {
210
+ if (!B64URL_PATTERN.test(rawSig)) {
211
+ throwErr("EXTERNAL_IDP_SIGNATURE_INVALID_001", "signature has invalid base64url characters");
212
+ }
213
+ const sigBytes = Buffer.from(rawSig, "base64url");
214
+ const data = Buffer.from(headerDotPayload, "ascii");
215
+ let ok;
216
+ try {
217
+ ok = cryptoVerify("RSA-SHA256", data, publicKey, sigBytes);
218
+ }
219
+ catch {
220
+ throwErr("EXTERNAL_IDP_SIGNATURE_INVALID_001", "signature verification error");
221
+ }
222
+ if (!ok) {
223
+ throwErr("EXTERNAL_IDP_SIGNATURE_INVALID_001", "signature verification failed");
224
+ }
225
+ }
226
+ //# sourceMappingURL=jwt-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-core.js","sourceRoot":"","sources":["../../src/auth/jwt-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { AuthAdapter, Clock, VerifiedPrincipal } from "./types.js";
2
+ import type { StaticJwtKeyRing } from "./static-jwt-key-ring.js";
3
+ export interface JwtKeyRingAuthAdapterConfig {
4
+ readonly keyRing: StaticJwtKeyRing;
5
+ readonly expectedIssuer: string;
6
+ readonly expectedAudience: string;
7
+ readonly clock?: Clock;
8
+ }
9
+ export declare class JwtKeyRingAuthAdapter implements AuthAdapter {
10
+ private readonly _keyRing;
11
+ private readonly _issuer;
12
+ private readonly _audience;
13
+ private readonly _clock;
14
+ constructor(config: JwtKeyRingAuthAdapterConfig);
15
+ verify(credential: string): Promise<VerifiedPrincipal>;
16
+ }
@@ -0,0 +1,58 @@
1
+ // src/auth/jwt-key-ring-auth-adapter.ts
2
+ // TEST/DEV only — NOT exported from src/index.ts.
3
+ // Key-ring mode: token MUST carry kid; secret selected from StaticJwtKeyRing.
4
+ // HS256 only. Verifier only — no signing. Secret injection is always caller-owned.
5
+ //
6
+ // AUTH_JWT_KEY_ID_REQUIRED_001 ✅ Production-04-a
7
+ //
8
+ // See docs/specs/production-04-jwt-secret-custody-spec.md §5.
9
+ import { throwErr, splitJwt, parseJsonObject, assertAlgHS256, verifyHmacSig, verifyPayloadAndBuildPrincipal, } from "./jwt-core.js";
10
+ // ─── JwtKeyRingAuthAdapter ────────────────────────────────────────────────────
11
+ export class JwtKeyRingAuthAdapter {
12
+ _keyRing;
13
+ _issuer;
14
+ _audience;
15
+ _clock;
16
+ constructor(config) {
17
+ if (config.keyRing == null) {
18
+ throwErr("AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001", "keyRing must be provided");
19
+ }
20
+ if (!config.expectedIssuer) {
21
+ throwErr("AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001", "expectedIssuer must be a non-empty string");
22
+ }
23
+ if (!config.expectedAudience) {
24
+ throwErr("AUTH_CREDENTIAL_KEY_CONFIG_INVALID_001", "expectedAudience must be a non-empty string");
25
+ }
26
+ this._keyRing = config.keyRing;
27
+ this._issuer = config.expectedIssuer;
28
+ this._audience = config.expectedAudience;
29
+ this._clock = config.clock ?? { now: () => new Date().toISOString() };
30
+ }
31
+ async verify(credential) {
32
+ // Compute clock once — used for both key temporal checks and token exp/nbf
33
+ const nowMs = Date.parse(this._clock.now());
34
+ // Step 1 — structure: exactly 3 non-empty segments
35
+ const [rawHeader, rawPayload, rawSig] = splitJwt(credential);
36
+ // Steps 2-3 — header
37
+ const h = parseJsonObject(rawHeader, "header");
38
+ // Step 4 — algorithm enforcement (alg: none / RS256 / etc. → rejected)
39
+ assertAlgHS256(h);
40
+ // Step 5 — kid required; key-ring mode never falls back to a default secret
41
+ // (AUTH_JWT_KEY_ID_REQUIRED_001)
42
+ if (!("kid" in h)) {
43
+ throwErr("AUTH_JWT_KEY_ID_REQUIRED_001", "key-ring mode requires kid in token header");
44
+ }
45
+ const kid = h["kid"];
46
+ if (typeof kid !== "string" || kid.length === 0) {
47
+ throwErr("AUTH_JWT_KEY_ID_REQUIRED_001", "kid must be a non-empty string");
48
+ }
49
+ // Step 6 — resolve key from ring (throws on unknown / revoked / not-yet-active / expired)
50
+ const secret = this._keyRing.resolveKey(kid, nowMs);
51
+ // Steps 7-8 — HMAC-SHA256 with timingSafeEqual (via jwt-core.ts)
52
+ verifyHmacSig(rawHeader, rawPayload, rawSig, secret);
53
+ // Steps 9-21 — payload parse + full verification + VerifiedPrincipal assembly
54
+ const p = parseJsonObject(rawPayload, "payload");
55
+ return verifyPayloadAndBuildPrincipal(p, nowMs, this._issuer, this._audience);
56
+ }
57
+ }
58
+ //# sourceMappingURL=jwt-key-ring-auth-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-key-ring-auth-adapter.js","sourceRoot":"","sources":["../../src/auth/jwt-key-ring-auth-adapter.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,kDAAkD;AAClD,8EAA8E;AAC9E,mFAAmF;AACnF,EAAE;AACF,iDAAiD;AACjD,EAAE;AACF,8DAA8D;AAG9D,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,cAAc,EACd,aAAa,EACb,8BAA8B,GAC/B,MAAM,eAAe,CAAC;AAYvB,iFAAiF;AAEjF,MAAM,OAAO,qBAAqB;IACf,QAAQ,CAAoB;IAC5B,OAAO,CAAW;IAClB,SAAS,CAAS;IAClB,MAAM,CAAW;IAElC,YAAY,MAAmC;QAC7C,IAAI,MAAM,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YAC3B,QAAQ,CAAC,wCAAwC,EAAE,0BAA0B,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAC3B,QAAQ,CAAC,wCAAwC,EAAE,2CAA2C,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC7B,QAAQ,CAAC,wCAAwC,EAAE,6CAA6C,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,CAAC,QAAQ,GAAI,MAAM,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,OAAO,GAAK,MAAM,CAAC,cAAc,CAAC;QACvC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,gBAAgB,CAAC;QACzC,IAAI,CAAC,MAAM,GAAM,MAAM,CAAC,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,UAAkB;QAC7B,2EAA2E;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QAE5C,mDAAmD;QACnD,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QAE7D,qBAAqB;QACrB,MAAM,CAAC,GAAG,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAE/C,uEAAuE;QACvE,cAAc,CAAC,CAAC,CAAC,CAAC;QAElB,4EAA4E;QAC5E,iCAAiC;QACjC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;YAClB,QAAQ,CAAC,8BAA8B,EAAE,4CAA4C,CAAC,CAAC;QACzF,CAAC;QACD,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChD,QAAQ,CAAC,8BAA8B,EAAE,gCAAgC,CAAC,CAAC;QAC7E,CAAC;QAED,0FAA0F;QAC1F,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEpD,iEAAiE;QACjE,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAErD,8EAA8E;QAC9E,MAAM,CAAC,GAAG,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACjD,OAAO,8BAA8B,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAChF,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import type { HttpClient } from "./http-client.js";
2
+ /**
3
+ * Resolve the JWKS URI from an OIDC issuer's discovery document.
4
+ * Fetches {issuerUrl}/.well-known/openid-configuration and extracts jwks_uri.
5
+ *
6
+ * This is a one-time startup utility — not called per-request.
7
+ * The result (jwks_uri string) should be passed to JwksKeyRingAuthAdapterConfig.jwksUri.
8
+ *
9
+ * @throws EXTERNAL_IDP_DISCOVERY_FETCH_FAILED_001 on HTTP error or timeout.
10
+ * @throws EXTERNAL_IDP_DISCOVERY_JWKS_URI_MISSING_001 if jwks_uri is absent in document.
11
+ */
12
+ export declare function resolveJwksUri(issuerUrl: string, httpClient: HttpClient, options?: {
13
+ timeoutMs?: number;
14
+ maxBytes?: number;
15
+ }): Promise<string>;
@@ -0,0 +1,46 @@
1
+ // src/auth/oidc-discovery.ts
2
+ // INTERNAL — not exported from src/index.ts.
3
+ // Optional OIDC discovery utility — resolves jwks_uri from issuer metadata.
4
+ // The JwksKeyRingAuthAdapter does not call this internally;
5
+ // it accepts jwksUri directly. This is a host startup convenience only.
6
+ //
7
+ // See docs/specs/post-mvp-external-idp-oidc-spec.md §9
8
+ import { throwErr } from "./jwt-core.js";
9
+ /**
10
+ * Resolve the JWKS URI from an OIDC issuer's discovery document.
11
+ * Fetches {issuerUrl}/.well-known/openid-configuration and extracts jwks_uri.
12
+ *
13
+ * This is a one-time startup utility — not called per-request.
14
+ * The result (jwks_uri string) should be passed to JwksKeyRingAuthAdapterConfig.jwksUri.
15
+ *
16
+ * @throws EXTERNAL_IDP_DISCOVERY_FETCH_FAILED_001 on HTTP error or timeout.
17
+ * @throws EXTERNAL_IDP_DISCOVERY_JWKS_URI_MISSING_001 if jwks_uri is absent in document.
18
+ */
19
+ export async function resolveJwksUri(issuerUrl, httpClient, options) {
20
+ const timeoutMs = options?.timeoutMs ?? 5000;
21
+ const maxBytes = options?.maxBytes ?? 65536;
22
+ const discoveryUrl = issuerUrl.replace(/\/+$/, "") + "/.well-known/openid-configuration";
23
+ let responseText;
24
+ try {
25
+ responseText = await httpClient.getText(discoveryUrl, { timeoutMs, maxBytes });
26
+ }
27
+ catch {
28
+ throwErr("EXTERNAL_IDP_DISCOVERY_FETCH_FAILED_001", "OIDC discovery fetch failed");
29
+ }
30
+ let parsed;
31
+ try {
32
+ parsed = JSON.parse(responseText);
33
+ }
34
+ catch {
35
+ throwErr("EXTERNAL_IDP_DISCOVERY_FETCH_FAILED_001", "OIDC discovery response is not valid JSON");
36
+ }
37
+ if (typeof parsed !== "object" || parsed === null) {
38
+ throwErr("EXTERNAL_IDP_DISCOVERY_FETCH_FAILED_001", "OIDC discovery response is not an object");
39
+ }
40
+ const jwksUri = parsed["jwks_uri"];
41
+ if (typeof jwksUri !== "string" || jwksUri.length === 0) {
42
+ throwErr("EXTERNAL_IDP_DISCOVERY_JWKS_URI_MISSING_001", "jwks_uri absent from discovery document");
43
+ }
44
+ return jwksUri;
45
+ }
46
+ //# sourceMappingURL=oidc-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-discovery.js","sourceRoot":"","sources":["../../src/auth/oidc-discovery.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,6CAA6C;AAC7C,4EAA4E;AAC5E,4DAA4D;AAC5D,wEAAwE;AACxE,EAAE;AACF,uDAAuD;AAGvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAoB,EACpB,UAAwB,EACxB,OAAuD;IAEvD,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC;IAC7C,MAAM,QAAQ,GAAI,OAAO,EAAE,QAAQ,IAAK,KAAK,CAAC;IAE9C,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,mCAAmC,CAAC;IAEzF,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,CAAC,yCAAyC,EAAE,6BAA6B,CAAC,CAAC;IACrF,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,QAAQ,CAAC,yCAAyC,EAAE,2CAA2C,CAAC,CAAC;IACnG,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAClD,QAAQ,CAAC,yCAAyC,EAAE,0CAA0C,CAAC,CAAC;IAClG,CAAC;IAED,MAAM,OAAO,GAAI,MAAkC,CAAC,UAAU,CAAC,CAAC;IAChE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,QAAQ,CAAC,6CAA6C,EAAE,yCAAyC,CAAC,CAAC;IACrG,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,58 @@
1
+ import type { PersonId, ProposalId } from "../types/ids.js";
2
+ import type { AcceptOptions, Proposal, RejectOptions, ProposalInput, ProposalStatus } from "../types/proposal.js";
3
+ import type { QueryContractsInput, ContextQueryResult } from "../surface/evidence-backed-context-query.js";
4
+ import type { ErasureStatus } from "../erasure/tombstone.js";
5
+ import type { LifeApi } from "../sdk/life-api.js";
6
+ import type { MintedContext, GuardianContextMintingBoundary } from "./types.js";
7
+ import type { AuditLog } from "../audit/audit-log.js";
8
+ import type { UnitOfWork } from "../persistence/unit-of-work.js";
9
+ import type { Logger } from "../observability/logger.js";
10
+ import type { MetricsRecorder } from "../observability/metrics.js";
11
+ import type { HealthCheckResult } from "../observability/health.js";
12
+ export interface ListProposalsInput {
13
+ readonly status?: ProposalStatus;
14
+ }
15
+ export interface ProductionScopedLifeApi {
16
+ requestErasure(subject: PersonId): Promise<void>;
17
+ getErasureStatus(subject: PersonId): Promise<ErasureStatus | undefined>;
18
+ queryContracts(input: QueryContractsInput): Promise<ContextQueryResult>;
19
+ acceptProposal(proposalId: ProposalId, options: AcceptOptions): Promise<Proposal>;
20
+ rejectProposal(proposalId: ProposalId, options: RejectOptions): Promise<Proposal>;
21
+ createProposal(input: ProposalInput): Promise<Proposal>;
22
+ getProposal(proposalId: ProposalId): Promise<Proposal | undefined>;
23
+ listProposals(input?: ListProposalsInput): Promise<readonly Proposal[]>;
24
+ }
25
+ export type ProductionApiBundle = {
26
+ readonly auth: GuardianContextMintingBoundary;
27
+ readonly createScopedApi: (mintedCtx: MintedContext) => ProductionScopedLifeApi;
28
+ readonly healthCheck: () => Promise<HealthCheckResult>;
29
+ };
30
+ export declare function assertOwnerSelfForErasure(subject: PersonId, mintedCtx: MintedContext): void;
31
+ export declare function assertOwnerOwnsProposalSubjectForReject(proposal: Proposal, mintedCtx: MintedContext): void;
32
+ export declare class ProductionScopedLifeApiImpl implements ProductionScopedLifeApi {
33
+ private readonly _api;
34
+ private readonly _minted;
35
+ private readonly _audit;
36
+ private readonly _mutationLog;
37
+ private readonly _uow;
38
+ private readonly _logger?;
39
+ private readonly _metricsRecorder?;
40
+ private readonly _subjectApi;
41
+ private readonly _proposalApi;
42
+ constructor(_api: LifeApi, _minted: MintedContext, _audit: AuditLog, _mutationLog: AuditLog, _uow: UnitOfWork, _logger?: Logger | undefined, _metricsRecorder?: MetricsRecorder | undefined);
43
+ private _emitProposalOutcome;
44
+ private _writeAuditOrFail;
45
+ private _actorLabel;
46
+ private _now;
47
+ private _guardianFields;
48
+ private _assertGuardianScope;
49
+ private _denyGuardianErasure;
50
+ requestErasure(subject: PersonId): Promise<void>;
51
+ getErasureStatus(subject: PersonId): Promise<ErasureStatus | undefined>;
52
+ queryContracts(input: QueryContractsInput): Promise<ContextQueryResult>;
53
+ acceptProposal(proposalId: ProposalId, options: AcceptOptions): Promise<Proposal>;
54
+ createProposal(input: ProposalInput): Promise<Proposal>;
55
+ getProposal(proposalId: ProposalId): Promise<Proposal | undefined>;
56
+ listProposals(input?: ListProposalsInput): Promise<readonly Proposal[]>;
57
+ rejectProposal(proposalId: ProposalId, options: RejectOptions): Promise<Proposal>;
58
+ }