@emilia-protocol/gate 0.10.0 → 0.13.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 (419) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +272 -0
  3. package/action-control-manifest.js +3 -268
  4. package/action-escrow-custodian.js +4 -0
  5. package/action-escrow-evidence.js +4 -0
  6. package/action-escrow-package.js +4 -0
  7. package/action-escrow-postgres.js +4 -0
  8. package/action-escrow-state.js +4 -0
  9. package/action-escrow-verifiers.js +4 -0
  10. package/action-escrow.js +4 -0
  11. package/action-packs.js +3 -140
  12. package/adapters/_kit.js +3 -104
  13. package/adapters/aws.js +3 -108
  14. package/adapters/cloudflare.js +3 -54
  15. package/adapters/gcp.js +3 -70
  16. package/adapters/github.js +3 -109
  17. package/adapters/jira.js +3 -55
  18. package/adapters/k8s.js +3 -70
  19. package/adapters/linear.js +3 -55
  20. package/adapters/salesforce.js +3 -55
  21. package/adapters/stripe.js +3 -81
  22. package/adapters/supabase.js +3 -121
  23. package/adapters/terraform.js +3 -59
  24. package/adapters/vercel.js +3 -84
  25. package/aec-execution.js +3 -312
  26. package/breakglass.js +3 -582
  27. package/capability-receipt.js +4 -0
  28. package/cf1-conformance.js +3 -106
  29. package/challenge-store.js +3 -69
  30. package/control-plane.js +3 -340
  31. package/coverage.js +3 -721
  32. package/demo.mjs +1 -1
  33. package/deployment-attestation.js +3 -247
  34. package/dist/action-control-manifest.d.ts +82 -0
  35. package/dist/action-control-manifest.d.ts.map +1 -0
  36. package/dist/action-control-manifest.js +422 -0
  37. package/dist/action-control-manifest.js.map +1 -0
  38. package/dist/action-escrow-custodian.d.ts +74 -0
  39. package/dist/action-escrow-custodian.d.ts.map +1 -0
  40. package/dist/action-escrow-custodian.js +363 -0
  41. package/dist/action-escrow-custodian.js.map +1 -0
  42. package/dist/action-escrow-evidence.d.ts +130 -0
  43. package/dist/action-escrow-evidence.d.ts.map +1 -0
  44. package/dist/action-escrow-evidence.js +1012 -0
  45. package/dist/action-escrow-evidence.js.map +1 -0
  46. package/dist/action-escrow-package.d.ts +10 -0
  47. package/dist/action-escrow-package.d.ts.map +1 -0
  48. package/dist/action-escrow-package.js +1583 -0
  49. package/dist/action-escrow-package.js.map +1 -0
  50. package/dist/action-escrow-postgres.d.ts +62 -0
  51. package/dist/action-escrow-postgres.d.ts.map +1 -0
  52. package/dist/action-escrow-postgres.js +339 -0
  53. package/dist/action-escrow-postgres.js.map +1 -0
  54. package/dist/action-escrow-state.d.ts +141 -0
  55. package/dist/action-escrow-state.d.ts.map +1 -0
  56. package/dist/action-escrow-state.js +413 -0
  57. package/dist/action-escrow-state.js.map +1 -0
  58. package/dist/action-escrow-verifiers.d.ts +65 -0
  59. package/dist/action-escrow-verifiers.d.ts.map +1 -0
  60. package/dist/action-escrow-verifiers.js +421 -0
  61. package/dist/action-escrow-verifiers.js.map +1 -0
  62. package/dist/action-escrow.d.ts +112 -0
  63. package/dist/action-escrow.d.ts.map +1 -0
  64. package/dist/action-escrow.js +2661 -0
  65. package/dist/action-escrow.js.map +1 -0
  66. package/dist/action-packs.d.ts +54 -0
  67. package/dist/action-packs.d.ts.map +1 -0
  68. package/dist/action-packs.js +143 -0
  69. package/dist/action-packs.js.map +1 -0
  70. package/dist/adapters/_kit.d.ts +50 -0
  71. package/dist/adapters/_kit.d.ts.map +1 -0
  72. package/dist/adapters/_kit.js +98 -0
  73. package/dist/adapters/_kit.js.map +1 -0
  74. package/dist/adapters/aws.d.ts +158 -0
  75. package/dist/adapters/aws.d.ts.map +1 -0
  76. package/dist/adapters/aws.js +136 -0
  77. package/dist/adapters/aws.js.map +1 -0
  78. package/dist/adapters/cloudflare.d.ts +116 -0
  79. package/dist/adapters/cloudflare.d.ts.map +1 -0
  80. package/dist/adapters/cloudflare.js +75 -0
  81. package/dist/adapters/cloudflare.js.map +1 -0
  82. package/dist/adapters/gcp.d.ts +139 -0
  83. package/dist/adapters/gcp.d.ts.map +1 -0
  84. package/dist/adapters/gcp.js +69 -0
  85. package/dist/adapters/gcp.js.map +1 -0
  86. package/dist/adapters/github.d.ts +123 -0
  87. package/dist/adapters/github.d.ts.map +1 -0
  88. package/dist/adapters/github.js +105 -0
  89. package/dist/adapters/github.js.map +1 -0
  90. package/dist/adapters/jira.d.ts +109 -0
  91. package/dist/adapters/jira.d.ts.map +1 -0
  92. package/dist/adapters/jira.js +54 -0
  93. package/dist/adapters/jira.js.map +1 -0
  94. package/dist/adapters/k8s.d.ts +139 -0
  95. package/dist/adapters/k8s.d.ts.map +1 -0
  96. package/dist/adapters/k8s.js +69 -0
  97. package/dist/adapters/k8s.js.map +1 -0
  98. package/dist/adapters/linear.d.ts +109 -0
  99. package/dist/adapters/linear.d.ts.map +1 -0
  100. package/dist/adapters/linear.js +54 -0
  101. package/dist/adapters/linear.js.map +1 -0
  102. package/dist/adapters/salesforce.d.ts +109 -0
  103. package/dist/adapters/salesforce.d.ts.map +1 -0
  104. package/dist/adapters/salesforce.js +54 -0
  105. package/dist/adapters/salesforce.js.map +1 -0
  106. package/dist/adapters/stripe.d.ts +116 -0
  107. package/dist/adapters/stripe.d.ts.map +1 -0
  108. package/dist/adapters/stripe.js +77 -0
  109. package/dist/adapters/stripe.js.map +1 -0
  110. package/dist/adapters/supabase.d.ts +127 -0
  111. package/dist/adapters/supabase.d.ts.map +1 -0
  112. package/dist/adapters/supabase.js +112 -0
  113. package/dist/adapters/supabase.js.map +1 -0
  114. package/dist/adapters/terraform.d.ts +109 -0
  115. package/dist/adapters/terraform.d.ts.map +1 -0
  116. package/dist/adapters/terraform.js +58 -0
  117. package/dist/adapters/terraform.js.map +1 -0
  118. package/dist/adapters/vercel.d.ts +114 -0
  119. package/dist/adapters/vercel.d.ts.map +1 -0
  120. package/dist/adapters/vercel.js +81 -0
  121. package/dist/adapters/vercel.js.map +1 -0
  122. package/dist/aec-execution.d.ts +107 -0
  123. package/dist/aec-execution.d.ts.map +1 -0
  124. package/dist/aec-execution.js +334 -0
  125. package/dist/aec-execution.js.map +1 -0
  126. package/dist/breakglass.d.ts +205 -0
  127. package/dist/breakglass.d.ts.map +1 -0
  128. package/dist/breakglass.js +627 -0
  129. package/dist/breakglass.js.map +1 -0
  130. package/dist/capability-receipt.d.ts +601 -0
  131. package/dist/capability-receipt.d.ts.map +1 -0
  132. package/dist/capability-receipt.js +1177 -0
  133. package/dist/capability-receipt.js.map +1 -0
  134. package/dist/cf1-conformance.d.ts +83 -0
  135. package/dist/cf1-conformance.d.ts.map +1 -0
  136. package/dist/cf1-conformance.js +94 -0
  137. package/dist/cf1-conformance.js.map +1 -0
  138. package/dist/challenge-store.d.ts +20 -0
  139. package/dist/challenge-store.d.ts.map +1 -0
  140. package/dist/challenge-store.js +62 -0
  141. package/dist/challenge-store.js.map +1 -0
  142. package/dist/control-plane.d.ts +136 -0
  143. package/dist/control-plane.d.ts.map +1 -0
  144. package/dist/control-plane.js +349 -0
  145. package/dist/control-plane.js.map +1 -0
  146. package/dist/coverage.d.ts +156 -0
  147. package/dist/coverage.d.ts.map +1 -0
  148. package/dist/coverage.js +768 -0
  149. package/dist/coverage.js.map +1 -0
  150. package/dist/deployment-attestation.d.ts +93 -0
  151. package/dist/deployment-attestation.d.ts.map +1 -0
  152. package/dist/deployment-attestation.js +262 -0
  153. package/dist/deployment-attestation.js.map +1 -0
  154. package/dist/eg1-conformance.d.ts +286 -0
  155. package/dist/eg1-conformance.d.ts.map +1 -0
  156. package/dist/eg1-conformance.js +379 -0
  157. package/dist/eg1-conformance.js.map +1 -0
  158. package/dist/enterprise.d.ts +88 -0
  159. package/dist/enterprise.d.ts.map +1 -0
  160. package/dist/enterprise.js +194 -0
  161. package/dist/enterprise.js.map +1 -0
  162. package/dist/evidence-postgres.d.ts +123 -0
  163. package/dist/evidence-postgres.d.ts.map +1 -0
  164. package/dist/evidence-postgres.js +343 -0
  165. package/dist/evidence-postgres.js.map +1 -0
  166. package/dist/evidence.d.ts +113 -0
  167. package/dist/evidence.d.ts.map +1 -0
  168. package/dist/evidence.js +369 -0
  169. package/dist/evidence.js.map +1 -0
  170. package/dist/execution-binding.d.ts +49 -0
  171. package/dist/execution-binding.d.ts.map +1 -0
  172. package/dist/execution-binding.js +243 -0
  173. package/dist/execution-binding.js.map +1 -0
  174. package/dist/formal-runtime-map.d.ts +42 -0
  175. package/dist/formal-runtime-map.d.ts.map +1 -0
  176. package/dist/formal-runtime-map.js +23 -0
  177. package/dist/formal-runtime-map.js.map +1 -0
  178. package/dist/index.d.ts +1002 -0
  179. package/dist/index.d.ts.map +1 -0
  180. package/dist/index.js +1736 -0
  181. package/dist/index.js.map +1 -0
  182. package/dist/key-registry.d.ts +60 -0
  183. package/dist/key-registry.d.ts.map +1 -0
  184. package/dist/key-registry.js +144 -0
  185. package/dist/key-registry.js.map +1 -0
  186. package/dist/mcp.d.ts +53 -0
  187. package/dist/mcp.d.ts.map +1 -0
  188. package/dist/mcp.js +112 -0
  189. package/dist/mcp.js.map +1 -0
  190. package/dist/metering.d.ts +80 -0
  191. package/dist/metering.d.ts.map +1 -0
  192. package/dist/metering.js +227 -0
  193. package/dist/metering.js.map +1 -0
  194. package/dist/metrics.d.ts +68 -0
  195. package/dist/metrics.d.ts.map +1 -0
  196. package/dist/metrics.js +242 -0
  197. package/dist/metrics.js.map +1 -0
  198. package/dist/network-witness.d.ts +167 -0
  199. package/dist/network-witness.d.ts.map +1 -0
  200. package/dist/network-witness.js +520 -0
  201. package/dist/network-witness.js.map +1 -0
  202. package/dist/protocol-state.d.ts +141 -0
  203. package/dist/protocol-state.d.ts.map +1 -0
  204. package/dist/protocol-state.js +106 -0
  205. package/dist/protocol-state.js.map +1 -0
  206. package/dist/receipt-program.d.ts +59 -0
  207. package/dist/receipt-program.d.ts.map +1 -0
  208. package/dist/receipt-program.js +1053 -0
  209. package/dist/receipt-program.js.map +1 -0
  210. package/dist/reliance-kernel.d.ts +66 -0
  211. package/dist/reliance-kernel.d.ts.map +1 -0
  212. package/dist/reliance-kernel.js +103 -0
  213. package/dist/reliance-kernel.js.map +1 -0
  214. package/dist/reliance-packet.d.ts +67 -0
  215. package/dist/reliance-packet.d.ts.map +1 -0
  216. package/dist/reliance-packet.js +176 -0
  217. package/dist/reliance-packet.js.map +1 -0
  218. package/dist/remedy-program-postgres.d.ts +37 -0
  219. package/dist/remedy-program-postgres.d.ts.map +1 -0
  220. package/dist/remedy-program-postgres.js +291 -0
  221. package/dist/remedy-program-postgres.js.map +1 -0
  222. package/dist/remedy-program-receipt.d.ts +97 -0
  223. package/dist/remedy-program-receipt.d.ts.map +1 -0
  224. package/dist/remedy-program-receipt.js +817 -0
  225. package/dist/remedy-program-receipt.js.map +1 -0
  226. package/dist/remedy-program.d.ts +76 -0
  227. package/dist/remedy-program.d.ts.map +1 -0
  228. package/dist/remedy-program.js +1346 -0
  229. package/dist/remedy-program.js.map +1 -0
  230. package/dist/reports/art14.d.ts +101 -0
  231. package/dist/reports/art14.d.ts.map +1 -0
  232. package/dist/reports/art14.js +340 -0
  233. package/dist/reports/art14.js.map +1 -0
  234. package/dist/reports/assurance-package.d.ts +197 -0
  235. package/dist/reports/assurance-package.d.ts.map +1 -0
  236. package/dist/reports/assurance-package.js +313 -0
  237. package/dist/reports/assurance-package.js.map +1 -0
  238. package/dist/reports/auditor-workpaper.d.ts +194 -0
  239. package/dist/reports/auditor-workpaper.d.ts.map +1 -0
  240. package/dist/reports/auditor-workpaper.js +545 -0
  241. package/dist/reports/auditor-workpaper.js.map +1 -0
  242. package/dist/reports/external-verification.d.ts +76 -0
  243. package/dist/reports/external-verification.d.ts.map +1 -0
  244. package/dist/reports/external-verification.js +209 -0
  245. package/dist/reports/external-verification.js.map +1 -0
  246. package/dist/reports/reperform.d.ts +122 -0
  247. package/dist/reports/reperform.d.ts.map +1 -0
  248. package/dist/reports/reperform.js +432 -0
  249. package/dist/reports/reperform.js.map +1 -0
  250. package/dist/reports/underwriter.d.ts +127 -0
  251. package/dist/reports/underwriter.d.ts.map +1 -0
  252. package/dist/reports/underwriter.js +348 -0
  253. package/dist/reports/underwriter.js.map +1 -0
  254. package/dist/retention.d.ts +88 -0
  255. package/dist/retention.d.ts.map +1 -0
  256. package/dist/retention.js +90 -0
  257. package/dist/retention.js.map +1 -0
  258. package/dist/roster.d.ts +113 -0
  259. package/dist/roster.d.ts.map +1 -0
  260. package/dist/roster.js +261 -0
  261. package/dist/roster.js.map +1 -0
  262. package/dist/runtime-monitor.d.ts +132 -0
  263. package/dist/runtime-monitor.d.ts.map +1 -0
  264. package/dist/runtime-monitor.js +197 -0
  265. package/dist/runtime-monitor.js.map +1 -0
  266. package/dist/settlement.d.ts +68 -0
  267. package/dist/settlement.d.ts.map +1 -0
  268. package/dist/settlement.js +306 -0
  269. package/dist/settlement.js.map +1 -0
  270. package/dist/siem.d.ts +261 -0
  271. package/dist/siem.d.ts.map +1 -0
  272. package/dist/siem.js +247 -0
  273. package/dist/siem.js.map +1 -0
  274. package/dist/store-postgres.d.ts +107 -0
  275. package/dist/store-postgres.d.ts.map +1 -0
  276. package/dist/store-postgres.js +152 -0
  277. package/dist/store-postgres.js.map +1 -0
  278. package/dist/store.d.ts +95 -0
  279. package/dist/store.d.ts.map +1 -0
  280. package/dist/store.js +202 -0
  281. package/dist/store.js.map +1 -0
  282. package/dist/strict-json.d.ts +14 -0
  283. package/dist/strict-json.d.ts.map +1 -0
  284. package/dist/strict-json.js +132 -0
  285. package/dist/strict-json.js.map +1 -0
  286. package/dist/trust-program-adapters.d.ts +122 -0
  287. package/dist/trust-program-adapters.d.ts.map +1 -0
  288. package/dist/trust-program-adapters.js +802 -0
  289. package/dist/trust-program-adapters.js.map +1 -0
  290. package/dist/trust-program-postgres.d.ts +41 -0
  291. package/dist/trust-program-postgres.d.ts.map +1 -0
  292. package/dist/trust-program-postgres.js +346 -0
  293. package/dist/trust-program-postgres.js.map +1 -0
  294. package/dist/trust-program-revocation.d.ts +206 -0
  295. package/dist/trust-program-revocation.d.ts.map +1 -0
  296. package/dist/trust-program-revocation.js +600 -0
  297. package/dist/trust-program-revocation.js.map +1 -0
  298. package/dist/trust-program.d.ts +197 -0
  299. package/dist/trust-program.d.ts.map +1 -0
  300. package/dist/trust-program.js +1351 -0
  301. package/dist/trust-program.js.map +1 -0
  302. package/dist/witness-postgres.d.ts +43 -0
  303. package/dist/witness-postgres.d.ts.map +1 -0
  304. package/dist/witness-postgres.js +92 -0
  305. package/dist/witness-postgres.js.map +1 -0
  306. package/dist/zk-range-proof.d.ts +78 -0
  307. package/dist/zk-range-proof.d.ts.map +1 -0
  308. package/dist/zk-range-proof.js +228 -0
  309. package/dist/zk-range-proof.js.map +1 -0
  310. package/eg1-conformance.js +3 -404
  311. package/enterprise.js +3 -180
  312. package/ep-assure.mjs +1 -0
  313. package/evidence-postgres.js +3 -356
  314. package/evidence.js +4 -360
  315. package/execution-binding.js +3 -212
  316. package/formal-runtime-map.js +4 -0
  317. package/index.js +4 -1249
  318. package/key-registry.js +3 -134
  319. package/mcp.js +3 -109
  320. package/metering.js +3 -226
  321. package/metrics.js +3 -231
  322. package/network-witness.js +3 -499
  323. package/package.json +47 -4
  324. package/receipt-program.js +4 -0
  325. package/reliance-kernel.js +3 -112
  326. package/reliance-packet.js +3 -175
  327. package/remedy-program-postgres.js +4 -0
  328. package/remedy-program-receipt.js +4 -0
  329. package/remedy-program.js +4 -0
  330. package/reports/art14.js +0 -0
  331. package/reports/assurance-package.js +2 -312
  332. package/reports/auditor-workpaper.js +3 -537
  333. package/reports/external-verification.js +3 -222
  334. package/reports/reperform.js +3 -424
  335. package/reports/underwriter.js +3 -339
  336. package/retention.js +3 -84
  337. package/roster.js +3 -264
  338. package/runtime-monitor.js +4 -0
  339. package/settlement.js +3 -299
  340. package/siem.js +3 -235
  341. package/src/action-control-manifest.ts +424 -0
  342. package/src/action-escrow-custodian.ts +427 -0
  343. package/src/action-escrow-evidence.ts +1178 -0
  344. package/src/action-escrow-package.ts +1821 -0
  345. package/src/action-escrow-postgres.ts +366 -0
  346. package/src/action-escrow-state.ts +498 -0
  347. package/src/action-escrow-verifiers.ts +484 -0
  348. package/src/action-escrow.ts +3039 -0
  349. package/src/action-packs.ts +170 -0
  350. package/src/adapters/_kit.ts +117 -0
  351. package/src/adapters/aws.ts +142 -0
  352. package/src/adapters/cloudflare.ts +78 -0
  353. package/src/adapters/gcp.ts +71 -0
  354. package/src/adapters/github.ts +110 -0
  355. package/src/adapters/jira.ts +56 -0
  356. package/src/adapters/k8s.ts +71 -0
  357. package/src/adapters/linear.ts +56 -0
  358. package/src/adapters/salesforce.ts +56 -0
  359. package/src/adapters/stripe.ts +82 -0
  360. package/src/adapters/supabase.ts +122 -0
  361. package/src/adapters/terraform.ts +60 -0
  362. package/src/adapters/vercel.ts +85 -0
  363. package/src/aec-execution.ts +354 -0
  364. package/src/breakglass.ts +647 -0
  365. package/src/capability-receipt.ts +1258 -0
  366. package/src/cf1-conformance.ts +120 -0
  367. package/src/challenge-store.ts +70 -0
  368. package/src/control-plane.ts +374 -0
  369. package/src/coverage.ts +832 -0
  370. package/src/deployment-attestation.ts +259 -0
  371. package/src/eg1-conformance.ts +471 -0
  372. package/src/enterprise.ts +195 -0
  373. package/src/evidence-postgres.ts +374 -0
  374. package/src/evidence.ts +367 -0
  375. package/src/execution-binding.ts +236 -0
  376. package/src/formal-runtime-map.ts +24 -0
  377. package/src/index.ts +2122 -0
  378. package/src/key-registry.ts +150 -0
  379. package/src/mcp.ts +120 -0
  380. package/src/metering.ts +237 -0
  381. package/src/metrics.ts +232 -0
  382. package/src/network-witness.ts +585 -0
  383. package/src/protocol-state.ts +143 -0
  384. package/src/protocol-state.typecheck.ts +94 -0
  385. package/src/receipt-program.ts +1105 -0
  386. package/src/reliance-kernel.ts +119 -0
  387. package/src/reliance-packet.ts +206 -0
  388. package/src/remedy-program-postgres.ts +367 -0
  389. package/src/remedy-program-receipt.ts +891 -0
  390. package/src/remedy-program.ts +1388 -0
  391. package/src/reports/art14.ts +361 -0
  392. package/src/reports/assurance-package.ts +330 -0
  393. package/src/reports/auditor-workpaper.ts +558 -0
  394. package/src/reports/external-verification.ts +232 -0
  395. package/src/reports/reperform.ts +441 -0
  396. package/src/reports/underwriter.ts +356 -0
  397. package/src/retention.ts +100 -0
  398. package/src/roster.ts +273 -0
  399. package/src/runtime-monitor.ts +217 -0
  400. package/src/settlement.ts +313 -0
  401. package/src/siem.ts +247 -0
  402. package/src/store-postgres.ts +167 -0
  403. package/src/store.ts +214 -0
  404. package/src/strict-json.ts +115 -0
  405. package/src/trust-program-adapters.ts +847 -0
  406. package/src/trust-program-postgres.ts +429 -0
  407. package/src/trust-program-revocation.ts +751 -0
  408. package/src/trust-program.ts +1460 -0
  409. package/src/witness-postgres.ts +106 -0
  410. package/src/zk-range-proof.ts +250 -0
  411. package/store-postgres.js +3 -161
  412. package/store.js +4 -198
  413. package/strict-json.js +3 -85
  414. package/trust-program-adapters.js +4 -0
  415. package/trust-program-postgres.js +4 -0
  416. package/trust-program-revocation.js +4 -0
  417. package/trust-program.js +4 -0
  418. package/witness-postgres.js +3 -96
  419. package/zk-range-proof.js +4 -0
package/src/metrics.ts ADDED
@@ -0,0 +1,232 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * EMILIA Gate — Prometheus metrics exposition (EP-GATE-METRICS-v1).
4
+ *
5
+ * Zero-dependency operational metrics over gate decisions, rendered in the
6
+ * Prometheus text exposition format 0.0.4 (HELP/TYPE lines, escaped label
7
+ * values, stable sorted output). Feed it the gate's decision entries — either
8
+ * the evidence-log entry ({ allow, action, reason, have_tier/required_tier,
9
+ * at }) or the `check()` output — via `onDecision`; scrape via `render()` or
10
+ * mount `handler()` on any framework.
11
+ *
12
+ * CONSTRAINT (enforced): metrics must NEVER throw into the enforcement path.
13
+ * `onDecision` sits on the gate's decision hot path; observability is never
14
+ * allowed to break enforcement. Any entry it cannot interpret — wrong type,
15
+ * underivable outcome, even a poisoned object whose property getters throw —
16
+ * increments ep_gate_metrics_malformed_total and returns. No exception
17
+ * escapes `onDecision`, ever.
18
+ *
19
+ * CARDINALITY GUARD: the action_type label is capped to a bounded set via the
20
+ * `maxSeries` option (default 64 distinct values). Once the cap is reached,
21
+ * new action types bucket to action_type="_other" so a hostile or buggy
22
+ * caller cannot blow up the time-series database. All label values are also
23
+ * length-capped. outcome and reason_class are bounded by construction.
24
+ *
25
+ * Deterministic and pure: time is injected (`now`), used only when an entry
26
+ * carries no usable timestamp. Same entries in, same exposition text out.
27
+ */
28
+
29
+ export const METRICS_VERSION = 'EP-GATE-METRICS-v1';
30
+
31
+ /** Prometheus text exposition format 0.0.4 content type. */
32
+ export const METRICS_CONTENT_TYPE = 'text/plain; version=0.0.4; charset=utf-8';
33
+
34
+ /** Bounded denial reason classes — every gate refusal maps into exactly one. */
35
+ export const REASON_CLASSES = [
36
+ 'replay', // replay_refused — one-time consumption caught a reuse
37
+ 'receipt_missing', // receipt_required — guarded action arrived with no receipt
38
+ 'receipt_invalid', // receipt_rejected:* — signature/freshness/scope/id failures
39
+ 'assurance', // assurance_too_low | unknown_required_tier
40
+ 'execution_binding', // execution_binding_failed — claim != observed mutation
41
+ 'infrastructure', // evidence_log_failed | consumption_store_lacks_reserve
42
+ 'other', // anything this version does not model
43
+ ];
44
+
45
+ /** Longest label value we will expose; longer values are truncated (bounded exposition size). */
46
+ const MAX_LABEL_VALUE_LEN = 128;
47
+
48
+ /** Overflow bucket for the action_type cardinality guard. */
49
+ const OTHER_BUCKET = '_other';
50
+
51
+ /**
52
+ * Classify a gate denial reason string into one of REASON_CLASSES. Bounded by
53
+ * construction: unknown reasons land in 'other', never in a fresh label value.
54
+ * @param {string} reason e.g. 'replay_refused', 'receipt_rejected:bad_signature'
55
+ * @returns {string} one of REASON_CLASSES
56
+ */
57
+ export function classifyDenialReason(reason) {
58
+ if (typeof reason !== 'string') return 'other';
59
+ if (reason === 'replay_refused' || reason.includes('replay')) return 'replay';
60
+ if (reason === 'receipt_required') return 'receipt_missing';
61
+ if (reason.startsWith('receipt_rejected')) return 'receipt_invalid';
62
+ if (reason === 'assurance_too_low' || reason === 'unknown_required_tier') return 'assurance';
63
+ if (reason === 'execution_binding_failed') return 'execution_binding';
64
+ if (reason === 'evidence_log_failed' || reason === 'consumption_store_lacks_reserve') return 'infrastructure';
65
+ return 'other';
66
+ }
67
+
68
+ /** Escape a label value per the exposition format: backslash, quote, newline. */
69
+ function escapeLabelValue(v) {
70
+ return String(v)
71
+ .replace(/\\/g, '\\\\')
72
+ .replace(/"/g, '\\"')
73
+ .replace(/\n/g, '\\n');
74
+ }
75
+
76
+ /** Escape HELP text per the exposition format: backslash and newline only. */
77
+ function escapeHelp(v) {
78
+ return String(v).replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
79
+ }
80
+
81
+ /** Normalize a raw value into a safe, length-capped label value. */
82
+ function normLabel(raw, fallback) {
83
+ if (typeof raw !== 'string' || raw.length === 0) return fallback;
84
+ return raw.length > MAX_LABEL_VALUE_LEN ? raw.slice(0, MAX_LABEL_VALUE_LEN) : raw;
85
+ }
86
+
87
+ /** Render a sorted, escaped label set: {a="x",b="y"} — stable key order. */
88
+ function renderLabels(obj) {
89
+ const keys = Object.keys(obj).sort();
90
+ return `{${keys.map((k) => `${k}="${escapeLabelValue(obj[k])}"`).join(',')}}`;
91
+ }
92
+
93
+ const METRICS = [
94
+ { name: 'ep_gate_decisions_total', type: 'counter', help: 'Gate decisions by outcome, action type, and credited assurance tier.' },
95
+ { name: 'ep_gate_denials_total', type: 'counter', help: 'Denied gate decisions by bounded denial reason class.' },
96
+ { name: 'ep_gate_evidence_entries_total', type: 'counter', help: 'Evidence-log entries recorded for gate decisions.' },
97
+ { name: 'ep_gate_last_decision_timestamp_seconds', type: 'gauge', help: 'Unix timestamp (seconds) of the most recent gate decision.' },
98
+ { name: 'ep_gate_metrics_malformed_total', type: 'counter', help: 'Decision entries the metrics layer could not interpret (dropped, never thrown).' },
99
+ { name: 'ep_gate_replays_blocked_total', type: 'counter', help: 'Receipt replays refused by one-time consumption.' },
100
+ ];
101
+
102
+ /**
103
+ * Create a metrics registry for one gate.
104
+ * @param {object} [o]
105
+ * @param {number} [o.maxSeries=64] cap on distinct action_type label values; overflow buckets to "_other"
106
+ * @param {number|function} [o.now=Date.now] injected clock (ms or () => ms) — used only when an entry has no usable timestamp
107
+ * @returns {{ onDecision: (entry: object) => void, render: () => string, handler: () => { status: number, headers: object, body: string } }}
108
+ */
109
+ export function createMetrics({ maxSeries = 64, now = Date.now } = {}) {
110
+ // Config is clamped, not thrown: a bad option must not brick metrics either.
111
+ const cap = (Number.isInteger(maxSeries) && maxSeries > 0) ? maxSeries : 64;
112
+
113
+ const decisions = new Map(); // rendered label set -> count
114
+ const denials = new Map(); // rendered label set -> count
115
+ let replaysBlocked = 0;
116
+ let evidenceEntries = 0;
117
+ let malformed = 0;
118
+ let lastDecisionSeconds: number | null = null; // gauge; no sample until the first decision
119
+
120
+ const actionTypes = new Set(); // cardinality guard for action_type
121
+
122
+ function boundedActionType(raw) {
123
+ const v = normLabel(raw, 'unknown');
124
+ if (actionTypes.has(v)) return v;
125
+ if (actionTypes.size < cap) { actionTypes.add(v); return v; }
126
+ return OTHER_BUCKET;
127
+ }
128
+
129
+ function inc(map, key) {
130
+ map.set(key, (map.get(key) || 0) + 1);
131
+ }
132
+
133
+ /**
134
+ * Observe one gate decision. NEVER throws — see the module constraint. A
135
+ * malformed entry (non-object, underivable outcome, throwing getters, ...)
136
+ * increments ep_gate_metrics_malformed_total and returns.
137
+ */
138
+ function onDecision(entry) {
139
+ try {
140
+ if (!entry || typeof entry !== 'object') { malformed += 1; return; }
141
+
142
+ // Outcome: the authoritative field is the boolean `allow` (evidence
143
+ // entry and check() output both carry it). A string outcome is accepted
144
+ // only if it is exactly 'allow'/'deny'. Anything else is malformed.
145
+ let outcome;
146
+ if (entry.allow === true) outcome = 'allow';
147
+ else if (entry.allow === false) outcome = 'deny';
148
+ else if (entry.outcome === 'allow' || entry.outcome === 'deny') outcome = entry.outcome;
149
+ else { malformed += 1; return; }
150
+
151
+ const actionType = boundedActionType(entry.action_type ?? entry.action);
152
+ const tier = normLabel(entry.tier ?? entry.have_tier ?? entry.required_tier, 'unknown');
153
+
154
+ inc(decisions, renderLabels({ outcome, action_type: actionType, tier }));
155
+
156
+ if (outcome === 'deny') {
157
+ const reasonClass = classifyDenialReason(entry.reason);
158
+ inc(denials, renderLabels({ reason_class: reasonClass }));
159
+ if (reasonClass === 'replay') replaysBlocked += 1;
160
+ }
161
+
162
+ // Each decision appends one evidence record — unless the entry says the
163
+ // write failed outright (check() output carries evidence: null then).
164
+ if (entry.evidence !== null) evidenceEntries += 1;
165
+
166
+ // Gauge: prefer the entry's own timestamp (`at` ISO/ms), else the
167
+ // injected clock. An unusable clock skips the gauge — never throws.
168
+ const at = entry.at ?? entry.created_at ?? null;
169
+ let ms: number | null = null;
170
+ if (typeof at === 'number' && Number.isFinite(at)) ms = at;
171
+ else if (typeof at === 'string') { const p = Date.parse(at); if (Number.isFinite(p)) ms = p; }
172
+ if (ms == null) {
173
+ try {
174
+ const n = typeof now === 'function' ? now() : now;
175
+ if (typeof n === 'number' && Number.isFinite(n)) ms = n;
176
+ } catch { /* unusable injected clock: skip the gauge update */ }
177
+ }
178
+ if (ms != null) lastDecisionSeconds = ms / 1000;
179
+ } catch {
180
+ // The never-throw guarantee: a poisoned entry (throwing getter, hostile
181
+ // proxy) lands here. Count it, drop it, return. Enforcement continues.
182
+ malformed += 1;
183
+ }
184
+ }
185
+
186
+ /** Render Prometheus text format 0.0.4. Deterministic: metrics in fixed order, series sorted. */
187
+ function render() {
188
+ const lines: string[] = [];
189
+ for (const m of METRICS) {
190
+ lines.push(`# HELP ${m.name} ${escapeHelp(m.help)}`);
191
+ lines.push(`# TYPE ${m.name} ${m.type}`);
192
+ switch (m.name) {
193
+ case 'ep_gate_decisions_total':
194
+ for (const k of [...decisions.keys()].sort()) lines.push(`${m.name}${k} ${decisions.get(k)}`);
195
+ break;
196
+ case 'ep_gate_denials_total':
197
+ for (const k of [...denials.keys()].sort()) lines.push(`${m.name}${k} ${denials.get(k)}`);
198
+ break;
199
+ case 'ep_gate_evidence_entries_total':
200
+ lines.push(`${m.name} ${evidenceEntries}`);
201
+ break;
202
+ case 'ep_gate_last_decision_timestamp_seconds':
203
+ // No sample until the first decision — a fabricated 0 would read as 1970.
204
+ if (lastDecisionSeconds != null) lines.push(`${m.name} ${lastDecisionSeconds}`);
205
+ break;
206
+ case 'ep_gate_metrics_malformed_total':
207
+ lines.push(`${m.name} ${malformed}`);
208
+ break;
209
+ case 'ep_gate_replays_blocked_total':
210
+ lines.push(`${m.name} ${replaysBlocked}`);
211
+ break;
212
+ /* c8 ignore next 2 -- METRICS is a module constant; no other names exist */
213
+ default:
214
+ break;
215
+ }
216
+ }
217
+ return lines.join('\n') + '\n';
218
+ }
219
+
220
+ /** Framework-agnostic scrape response: mount on any router/handler. */
221
+ function handler() {
222
+ return {
223
+ status: 200,
224
+ headers: { 'content-type': METRICS_CONTENT_TYPE },
225
+ body: render(),
226
+ };
227
+ }
228
+
229
+ return { onDecision, render, handler };
230
+ }
231
+
232
+ export default { createMetrics, classifyDenialReason, METRICS_VERSION, METRICS_CONTENT_TYPE, REASON_CLASSES };