@blamejs/blamejs-shop 0.0.44

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 (1220) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +17 -0
  3. package/README.md +117 -0
  4. package/SECURITY.md +139 -0
  5. package/lib/admin.js +952 -0
  6. package/lib/analytics.js +267 -0
  7. package/lib/cart.js +279 -0
  8. package/lib/catalog-import.js +344 -0
  9. package/lib/catalog.js +769 -0
  10. package/lib/checkout.js +320 -0
  11. package/lib/config.js +151 -0
  12. package/lib/customers.js +322 -0
  13. package/lib/email.js +242 -0
  14. package/lib/externaldb-d1.js +283 -0
  15. package/lib/index.js +57 -0
  16. package/lib/inventory-alerts.js +198 -0
  17. package/lib/newsletter.js +142 -0
  18. package/lib/order.js +380 -0
  19. package/lib/payment.js +318 -0
  20. package/lib/pricing.js +185 -0
  21. package/lib/r2-bridge.js +169 -0
  22. package/lib/shipping.js +185 -0
  23. package/lib/storefront.js +2160 -0
  24. package/lib/subscriptions.js +410 -0
  25. package/lib/tax.js +161 -0
  26. package/lib/theme.js +194 -0
  27. package/lib/vendor/MANIFEST.json +19 -0
  28. package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +23 -0
  29. package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +34 -0
  30. package/lib/vendor/blamejs/.clusterfuzzlite/project.yaml +16 -0
  31. package/lib/vendor/blamejs/.dockerignore +45 -0
  32. package/lib/vendor/blamejs/.gitattributes +42 -0
  33. package/lib/vendor/blamejs/.github/CODEOWNERS +4 -0
  34. package/lib/vendor/blamejs/.github/FUNDING.yml +2 -0
  35. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/bug_report.md +58 -0
  36. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/config.yml +8 -0
  37. package/lib/vendor/blamejs/.github/ISSUE_TEMPLATE/feature_request.md +99 -0
  38. package/lib/vendor/blamejs/.github/PULL_REQUEST_TEMPLATE.md +77 -0
  39. package/lib/vendor/blamejs/.github/dependabot.yml +37 -0
  40. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +148 -0
  41. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +107 -0
  42. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +122 -0
  43. package/lib/vendor/blamejs/.github/workflows/ci.yml +511 -0
  44. package/lib/vendor/blamejs/.github/workflows/codeql.yml +50 -0
  45. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +655 -0
  46. package/lib/vendor/blamejs/.github/workflows/release-container.yml +406 -0
  47. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +101 -0
  48. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +134 -0
  49. package/lib/vendor/blamejs/.gitignore +102 -0
  50. package/lib/vendor/blamejs/.gitleaks.toml +166 -0
  51. package/lib/vendor/blamejs/.hadolint.yaml +18 -0
  52. package/lib/vendor/blamejs/.npmrc +5 -0
  53. package/lib/vendor/blamejs/.pinact.yaml +17 -0
  54. package/lib/vendor/blamejs/ARCHITECTURE.md +158 -0
  55. package/lib/vendor/blamejs/CHANGELOG.md +1351 -0
  56. package/lib/vendor/blamejs/CODE_OF_CONDUCT.md +86 -0
  57. package/lib/vendor/blamejs/CONTRIBUTING.md +156 -0
  58. package/lib/vendor/blamejs/GOVERNANCE.md +201 -0
  59. package/lib/vendor/blamejs/LICENSE +201 -0
  60. package/lib/vendor/blamejs/LTS-CALENDAR.md +29 -0
  61. package/lib/vendor/blamejs/MIGRATING.md +29 -0
  62. package/lib/vendor/blamejs/NOTICE +81 -0
  63. package/lib/vendor/blamejs/README.md +304 -0
  64. package/lib/vendor/blamejs/SECURITY.md +432 -0
  65. package/lib/vendor/blamejs/api-snapshot.json +48709 -0
  66. package/lib/vendor/blamejs/assets/BlameJS_Logo.png +0 -0
  67. package/lib/vendor/blamejs/assets/BlameJS_Logo.svg +129 -0
  68. package/lib/vendor/blamejs/bench/README.md +77 -0
  69. package/lib/vendor/blamejs/bench/_helpers.js +70 -0
  70. package/lib/vendor/blamejs/bench/baseline.json +183 -0
  71. package/lib/vendor/blamejs/bench/crypto-hash.bench.js +19 -0
  72. package/lib/vendor/blamejs/bench/crypto-symmetric.bench.js +28 -0
  73. package/lib/vendor/blamejs/bench/run.js +140 -0
  74. package/lib/vendor/blamejs/bench/safe-json.bench.js +31 -0
  75. package/lib/vendor/blamejs/bin/blamejs.js +13 -0
  76. package/lib/vendor/blamejs/docker/caddy/Caddyfile +46 -0
  77. package/lib/vendor/blamejs/docker/coredns/Corefile +37 -0
  78. package/lib/vendor/blamejs/docker/haproxy/haproxy.cfg +52 -0
  79. package/lib/vendor/blamejs/docker/init/generate-certs.sh +118 -0
  80. package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +87 -0
  81. package/lib/vendor/blamejs/docker/mitmproxy/config.yaml +16 -0
  82. package/lib/vendor/blamejs/docker/mongo/init-tls.sh +17 -0
  83. package/lib/vendor/blamejs/docker/mysql/my.cnf +12 -0
  84. package/lib/vendor/blamejs/docker/nats/nats.conf +33 -0
  85. package/lib/vendor/blamejs/docker/postgres/init-tls.sh +17 -0
  86. package/lib/vendor/blamejs/docker/postgres/postgresql.conf +18 -0
  87. package/lib/vendor/blamejs/docker/rabbitmq/rabbitmq.conf +18 -0
  88. package/lib/vendor/blamejs/docker/redis/redis.conf +15 -0
  89. package/lib/vendor/blamejs/docker/squid/squid.conf +24 -0
  90. package/lib/vendor/blamejs/docker/syslog/syslog-ng.conf +34 -0
  91. package/lib/vendor/blamejs/docker-compose.test.yml +545 -0
  92. package/lib/vendor/blamejs/docs/cis-postgres-crosswalk.md +102 -0
  93. package/lib/vendor/blamejs/docs/cis-sqlite-equivalent.md +92 -0
  94. package/lib/vendor/blamejs/eslint.config.mjs +204 -0
  95. package/lib/vendor/blamejs/examples/wiki/Caddyfile +40 -0
  96. package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +218 -0
  97. package/lib/vendor/blamejs/examples/wiki/Dockerfile +120 -0
  98. package/lib/vendor/blamejs/examples/wiki/README.md +157 -0
  99. package/lib/vendor/blamejs/examples/wiki/cli-snapshot.json +250 -0
  100. package/lib/vendor/blamejs/examples/wiki/docker-compose.prod.yml +231 -0
  101. package/lib/vendor/blamejs/examples/wiki/docker-compose.yml +166 -0
  102. package/lib/vendor/blamejs/examples/wiki/env-snapshot.json +217 -0
  103. package/lib/vendor/blamejs/examples/wiki/lib/auto-site-entries.js +139 -0
  104. package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +555 -0
  105. package/lib/vendor/blamejs/examples/wiki/lib/harvest-cli.js +507 -0
  106. package/lib/vendor/blamejs/examples/wiki/lib/harvest-env-vars.js +435 -0
  107. package/lib/vendor/blamejs/examples/wiki/lib/harvest-errors.js +282 -0
  108. package/lib/vendor/blamejs/examples/wiki/lib/harvest-vendored-deps.js +321 -0
  109. package/lib/vendor/blamejs/examples/wiki/lib/nav.js +15 -0
  110. package/lib/vendor/blamejs/examples/wiki/lib/opts-resolver.js +75 -0
  111. package/lib/vendor/blamejs/examples/wiki/lib/page-generator.js +508 -0
  112. package/lib/vendor/blamejs/examples/wiki/lib/section.js +276 -0
  113. package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +587 -0
  114. package/lib/vendor/blamejs/examples/wiki/lib/source-doc-parser.js +318 -0
  115. package/lib/vendor/blamejs/examples/wiki/lib/symbol-index.js +122 -0
  116. package/lib/vendor/blamejs/examples/wiki/migrations/0001-pages-schema.js +74 -0
  117. package/lib/vendor/blamejs/examples/wiki/package.json +18 -0
  118. package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.png +0 -0
  119. package/lib/vendor/blamejs/examples/wiki/public/img/blamejs-logo.svg +129 -0
  120. package/lib/vendor/blamejs/examples/wiki/public/robots.txt +5 -0
  121. package/lib/vendor/blamejs/examples/wiki/public/vendor/MANIFEST.json +30 -0
  122. package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.css +1 -0
  123. package/lib/vendor/blamejs/examples/wiki/public/vendor/prism.js +15 -0
  124. package/lib/vendor/blamejs/examples/wiki/public/wiki.css +1250 -0
  125. package/lib/vendor/blamejs/examples/wiki/routes/admin.js +366 -0
  126. package/lib/vendor/blamejs/examples/wiki/routes/integration.js +230 -0
  127. package/lib/vendor/blamejs/examples/wiki/routes/pages.js +266 -0
  128. package/lib/vendor/blamejs/examples/wiki/scripts/backfill-module-metadata.js +214 -0
  129. package/lib/vendor/blamejs/examples/wiki/seeders/prod/0001-default-pages.js +35 -0
  130. package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/_index.js +34 -0
  131. package/lib/vendor/blamejs/examples/wiki/seeders/prod/pages/api.js +76 -0
  132. package/lib/vendor/blamejs/examples/wiki/server.js +129 -0
  133. package/lib/vendor/blamejs/examples/wiki/site.config.js +197 -0
  134. package/lib/vendor/blamejs/examples/wiki/snippets/README.md +38 -0
  135. package/lib/vendor/blamejs/examples/wiki/snippets/auth/password-hash.example.js +15 -0
  136. package/lib/vendor/blamejs/examples/wiki/src/editor.js +103 -0
  137. package/lib/vendor/blamejs/examples/wiki/src/wiki.js +349 -0
  138. package/lib/vendor/blamejs/examples/wiki/test/AUDIT.md +155 -0
  139. package/lib/vendor/blamejs/examples/wiki/test/codebase-patterns.test.js +594 -0
  140. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +741 -0
  141. package/lib/vendor/blamejs/examples/wiki/test/find-missing-pages.js +254 -0
  142. package/lib/vendor/blamejs/examples/wiki/test/integration.js +391 -0
  143. package/lib/vendor/blamejs/examples/wiki/test/validate-cli-snapshot.js +379 -0
  144. package/lib/vendor/blamejs/examples/wiki/test/validate-env-snapshot.js +346 -0
  145. package/lib/vendor/blamejs/examples/wiki/test/validate-nav-coverage.js +212 -0
  146. package/lib/vendor/blamejs/examples/wiki/test/validate-site-coverage.js +252 -0
  147. package/lib/vendor/blamejs/examples/wiki/test/validate-source-comment-blocks.js +107 -0
  148. package/lib/vendor/blamejs/examples/wiki/views/_layout.html +115 -0
  149. package/lib/vendor/blamejs/examples/wiki/views/admin/api-keys.html +51 -0
  150. package/lib/vendor/blamejs/examples/wiki/views/admin/dashboard.html +22 -0
  151. package/lib/vendor/blamejs/examples/wiki/views/admin/edit.html +17 -0
  152. package/lib/vendor/blamejs/examples/wiki/views/home.html +85 -0
  153. package/lib/vendor/blamejs/examples/wiki/views/login.html +18 -0
  154. package/lib/vendor/blamejs/examples/wiki/views/page.html +5 -0
  155. package/lib/vendor/blamejs/examples/wiki/views/partials/nav.html +13 -0
  156. package/lib/vendor/blamejs/examples/wiki/views/search.html +19 -0
  157. package/lib/vendor/blamejs/examples/wiki/wiki.config.js +15 -0
  158. package/lib/vendor/blamejs/fuzz/README.md +137 -0
  159. package/lib/vendor/blamejs/fuzz/_expected.js +35 -0
  160. package/lib/vendor/blamejs/fuzz/guard-agent-registry.fuzz.js +22 -0
  161. package/lib/vendor/blamejs/fuzz/guard-csv.fuzz.js +16 -0
  162. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/01-basic.csv +3 -0
  163. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/02-formula.csv +1 -0
  164. package/lib/vendor/blamejs/fuzz/guard-csv_seed_corpus/03-hyperlink.csv +1 -0
  165. package/lib/vendor/blamejs/fuzz/guard-dsn.fuzz.js +22 -0
  166. package/lib/vendor/blamejs/fuzz/guard-email.fuzz.js +16 -0
  167. package/lib/vendor/blamejs/fuzz/guard-email_seed_corpus/01-basic.eml +5 -0
  168. package/lib/vendor/blamejs/fuzz/guard-envelope.fuzz.js +24 -0
  169. package/lib/vendor/blamejs/fuzz/guard-event-bus-payload.fuzz.js +24 -0
  170. package/lib/vendor/blamejs/fuzz/guard-event-bus-topic.fuzz.js +20 -0
  171. package/lib/vendor/blamejs/fuzz/guard-html.fuzz.js +16 -0
  172. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/01-basic.html +1 -0
  173. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/02-script.html +1 -0
  174. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/03-event.html +1 -0
  175. package/lib/vendor/blamejs/fuzz/guard-html_seed_corpus/04-jsurl.html +1 -0
  176. package/lib/vendor/blamejs/fuzz/guard-idempotency-key.fuzz.js +20 -0
  177. package/lib/vendor/blamejs/fuzz/guard-imap-command.fuzz.js +35 -0
  178. package/lib/vendor/blamejs/fuzz/guard-jmap.fuzz.js +41 -0
  179. package/lib/vendor/blamejs/fuzz/guard-json.fuzz.js +16 -0
  180. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/01-basic.json +1 -0
  181. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/02-proto.json +1 -0
  182. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/03-dupkey.json +1 -0
  183. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/04-nan.json +1 -0
  184. package/lib/vendor/blamejs/fuzz/guard-json_seed_corpus/05-bom.json +1 -0
  185. package/lib/vendor/blamejs/fuzz/guard-list-id.fuzz.js +21 -0
  186. package/lib/vendor/blamejs/fuzz/guard-list-unsubscribe.fuzz.js +25 -0
  187. package/lib/vendor/blamejs/fuzz/guard-mail-compose.fuzz.js +22 -0
  188. package/lib/vendor/blamejs/fuzz/guard-mail-move.fuzz.js +22 -0
  189. package/lib/vendor/blamejs/fuzz/guard-mail-query.fuzz.js +27 -0
  190. package/lib/vendor/blamejs/fuzz/guard-mail-reply.fuzz.js +23 -0
  191. package/lib/vendor/blamejs/fuzz/guard-mail-sieve.fuzz.js +36 -0
  192. package/lib/vendor/blamejs/fuzz/guard-managesieve-command.fuzz.js +26 -0
  193. package/lib/vendor/blamejs/fuzz/guard-markdown.fuzz.js +16 -0
  194. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/01-basic.md +2 -0
  195. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/02-jsurl.md +1 -0
  196. package/lib/vendor/blamejs/fuzz/guard-markdown_seed_corpus/03-jsimg.md +1 -0
  197. package/lib/vendor/blamejs/fuzz/guard-message-id.fuzz.js +26 -0
  198. package/lib/vendor/blamejs/fuzz/guard-pop3-command.fuzz.js +23 -0
  199. package/lib/vendor/blamejs/fuzz/guard-posture-chain.fuzz.js +22 -0
  200. package/lib/vendor/blamejs/fuzz/guard-saga-config.fuzz.js +32 -0
  201. package/lib/vendor/blamejs/fuzz/guard-smtp-command.fuzz.js +27 -0
  202. package/lib/vendor/blamejs/fuzz/guard-snapshot-envelope.fuzz.js +22 -0
  203. package/lib/vendor/blamejs/fuzz/guard-stream-args.fuzz.js +22 -0
  204. package/lib/vendor/blamejs/fuzz/guard-svg.fuzz.js +16 -0
  205. package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/01-basic.svg +1 -0
  206. package/lib/vendor/blamejs/fuzz/guard-svg_seed_corpus/02-script.svg +1 -0
  207. package/lib/vendor/blamejs/fuzz/guard-tenant-id.fuzz.js +20 -0
  208. package/lib/vendor/blamejs/fuzz/guard-trace-context.fuzz.js +30 -0
  209. package/lib/vendor/blamejs/fuzz/guard-xml.fuzz.js +16 -0
  210. package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/01-basic.xml +1 -0
  211. package/lib/vendor/blamejs/fuzz/guard-xml_seed_corpus/02-xxe.xml +1 -0
  212. package/lib/vendor/blamejs/fuzz/guard-yaml.fuzz.js +16 -0
  213. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/01-basic.yaml +2 -0
  214. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/02-anchor.yaml +2 -0
  215. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/03-norway.yaml +1 -0
  216. package/lib/vendor/blamejs/fuzz/guard-yaml_seed_corpus/04-multidoc.yaml +4 -0
  217. package/lib/vendor/blamejs/fuzz/parsers__safe-ini.fuzz.js +16 -0
  218. package/lib/vendor/blamejs/fuzz/parsers__safe-ini_seed_corpus/01-basic.ini +2 -0
  219. package/lib/vendor/blamejs/fuzz/parsers__safe-toml.fuzz.js +16 -0
  220. package/lib/vendor/blamejs/fuzz/parsers__safe-toml_seed_corpus/01-basic.toml +4 -0
  221. package/lib/vendor/blamejs/fuzz/parsers__safe-xml.fuzz.js +16 -0
  222. package/lib/vendor/blamejs/fuzz/parsers__safe-xml_seed_corpus/01-basic.xml +1 -0
  223. package/lib/vendor/blamejs/fuzz/parsers__safe-yaml.fuzz.js +16 -0
  224. package/lib/vendor/blamejs/fuzz/parsers__safe-yaml_seed_corpus/01-basic.yaml +4 -0
  225. package/lib/vendor/blamejs/fuzz/safe-decompress.fuzz.js +49 -0
  226. package/lib/vendor/blamejs/fuzz/safe-dns.fuzz.js +29 -0
  227. package/lib/vendor/blamejs/fuzz/safe-ical.fuzz.js +16 -0
  228. package/lib/vendor/blamejs/fuzz/safe-icap.fuzz.js +42 -0
  229. package/lib/vendor/blamejs/fuzz/safe-json.fuzz.js +25 -0
  230. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/01-object.txt +1 -0
  231. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/02-array.txt +1 -0
  232. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/03-string.txt +1 -0
  233. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/04-proto.txt +1 -0
  234. package/lib/vendor/blamejs/fuzz/safe-json_seed_corpus/05-deep.txt +1 -0
  235. package/lib/vendor/blamejs/fuzz/safe-jsonpath.fuzz.js +16 -0
  236. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/01-basic.txt +1 -0
  237. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/02-filter.txt +1 -0
  238. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/03-deepscan.txt +1 -0
  239. package/lib/vendor/blamejs/fuzz/safe-jsonpath_seed_corpus/04-slice.txt +1 -0
  240. package/lib/vendor/blamejs/fuzz/safe-mime.fuzz.js +27 -0
  241. package/lib/vendor/blamejs/fuzz/safe-mount-info.fuzz.js +33 -0
  242. package/lib/vendor/blamejs/fuzz/safe-sieve.fuzz.js +28 -0
  243. package/lib/vendor/blamejs/fuzz/safe-smtp.fuzz.js +64 -0
  244. package/lib/vendor/blamejs/fuzz/safe-url.fuzz.js +16 -0
  245. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/01-basic.txt +1 -0
  246. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/02-userinfo.txt +1 -0
  247. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/03-dangerous.txt +1 -0
  248. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/04-data.txt +1 -0
  249. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/05-ipv6.txt +1 -0
  250. package/lib/vendor/blamejs/fuzz/safe-url_seed_corpus/06-idn.txt +1 -0
  251. package/lib/vendor/blamejs/fuzz/safe-vcard.fuzz.js +16 -0
  252. package/lib/vendor/blamejs/index.js +678 -0
  253. package/lib/vendor/blamejs/keys/release-pqc-pub.json +7 -0
  254. package/lib/vendor/blamejs/lib/_test/crypto-fixtures.js +67 -0
  255. package/lib/vendor/blamejs/lib/a2a-tasks.js +598 -0
  256. package/lib/vendor/blamejs/lib/a2a.js +407 -0
  257. package/lib/vendor/blamejs/lib/acme.js +1448 -0
  258. package/lib/vendor/blamejs/lib/agent-audit.js +45 -0
  259. package/lib/vendor/blamejs/lib/agent-event-bus.js +382 -0
  260. package/lib/vendor/blamejs/lib/agent-idempotency.js +497 -0
  261. package/lib/vendor/blamejs/lib/agent-orchestrator.js +717 -0
  262. package/lib/vendor/blamejs/lib/agent-posture-chain.js +366 -0
  263. package/lib/vendor/blamejs/lib/agent-saga.js +321 -0
  264. package/lib/vendor/blamejs/lib/agent-snapshot.js +676 -0
  265. package/lib/vendor/blamejs/lib/agent-stream.js +269 -0
  266. package/lib/vendor/blamejs/lib/agent-tenant.js +632 -0
  267. package/lib/vendor/blamejs/lib/agent-trace.js +281 -0
  268. package/lib/vendor/blamejs/lib/ai-adverse-decision.js +184 -0
  269. package/lib/vendor/blamejs/lib/ai-content-detect.js +268 -0
  270. package/lib/vendor/blamejs/lib/ai-input.js +201 -0
  271. package/lib/vendor/blamejs/lib/ai-model-manifest.js +363 -0
  272. package/lib/vendor/blamejs/lib/ai-pref.js +340 -0
  273. package/lib/vendor/blamejs/lib/api-key.js +721 -0
  274. package/lib/vendor/blamejs/lib/api-snapshot.js +458 -0
  275. package/lib/vendor/blamejs/lib/app-shutdown.js +557 -0
  276. package/lib/vendor/blamejs/lib/app.js +365 -0
  277. package/lib/vendor/blamejs/lib/archive.js +547 -0
  278. package/lib/vendor/blamejs/lib/arg-parser.js +697 -0
  279. package/lib/vendor/blamejs/lib/argon2-builtin.js +173 -0
  280. package/lib/vendor/blamejs/lib/asn1-der.js +424 -0
  281. package/lib/vendor/blamejs/lib/asyncapi-bindings.js +160 -0
  282. package/lib/vendor/blamejs/lib/asyncapi-traits.js +143 -0
  283. package/lib/vendor/blamejs/lib/asyncapi.js +575 -0
  284. package/lib/vendor/blamejs/lib/atomic-file.js +1023 -0
  285. package/lib/vendor/blamejs/lib/audit-chain.js +266 -0
  286. package/lib/vendor/blamejs/lib/audit-daily-review.js +389 -0
  287. package/lib/vendor/blamejs/lib/audit-sign.js +751 -0
  288. package/lib/vendor/blamejs/lib/audit-tools.js +1113 -0
  289. package/lib/vendor/blamejs/lib/audit.js +1671 -0
  290. package/lib/vendor/blamejs/lib/auth/aal.js +169 -0
  291. package/lib/vendor/blamejs/lib/auth/access-lock.js +220 -0
  292. package/lib/vendor/blamejs/lib/auth/acr-vocabulary.js +265 -0
  293. package/lib/vendor/blamejs/lib/auth/ato-kill-switch.js +112 -0
  294. package/lib/vendor/blamejs/lib/auth/auth-time-tracker.js +111 -0
  295. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +573 -0
  296. package/lib/vendor/blamejs/lib/auth/ciba.js +637 -0
  297. package/lib/vendor/blamejs/lib/auth/dpop.js +516 -0
  298. package/lib/vendor/blamejs/lib/auth/elevation-grant.js +306 -0
  299. package/lib/vendor/blamejs/lib/auth/fal.js +229 -0
  300. package/lib/vendor/blamejs/lib/auth/fido-mds3.js +681 -0
  301. package/lib/vendor/blamejs/lib/auth/jwt-external.js +519 -0
  302. package/lib/vendor/blamejs/lib/auth/jwt.js +430 -0
  303. package/lib/vendor/blamejs/lib/auth/lockout.js +449 -0
  304. package/lib/vendor/blamejs/lib/auth/oauth.js +2141 -0
  305. package/lib/vendor/blamejs/lib/auth/oid4vci.js +657 -0
  306. package/lib/vendor/blamejs/lib/auth/oid4vp.js +531 -0
  307. package/lib/vendor/blamejs/lib/auth/openid-federation.js +600 -0
  308. package/lib/vendor/blamejs/lib/auth/passkey.js +676 -0
  309. package/lib/vendor/blamejs/lib/auth/password.js +693 -0
  310. package/lib/vendor/blamejs/lib/auth/saml.js +2109 -0
  311. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-disclosure.js +95 -0
  312. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +225 -0
  313. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-issuer.js +197 -0
  314. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +728 -0
  315. package/lib/vendor/blamejs/lib/auth/status-list.js +272 -0
  316. package/lib/vendor/blamejs/lib/auth/step-up-policy.js +335 -0
  317. package/lib/vendor/blamejs/lib/auth/step-up.js +454 -0
  318. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +505 -0
  319. package/lib/vendor/blamejs/lib/auth-header.js +148 -0
  320. package/lib/vendor/blamejs/lib/backup/bundle.js +265 -0
  321. package/lib/vendor/blamejs/lib/backup/crypto.js +176 -0
  322. package/lib/vendor/blamejs/lib/backup/index.js +1001 -0
  323. package/lib/vendor/blamejs/lib/backup/manifest.js +443 -0
  324. package/lib/vendor/blamejs/lib/boot-gates.js +174 -0
  325. package/lib/vendor/blamejs/lib/breach-deadline.js +272 -0
  326. package/lib/vendor/blamejs/lib/break-glass.js +1753 -0
  327. package/lib/vendor/blamejs/lib/budr.js +205 -0
  328. package/lib/vendor/blamejs/lib/bundler.js +461 -0
  329. package/lib/vendor/blamejs/lib/cache-redis.js +256 -0
  330. package/lib/vendor/blamejs/lib/cache-status.js +288 -0
  331. package/lib/vendor/blamejs/lib/cache.js +1331 -0
  332. package/lib/vendor/blamejs/lib/calendar.js +1240 -0
  333. package/lib/vendor/blamejs/lib/canonical-json.js +143 -0
  334. package/lib/vendor/blamejs/lib/cdn-cache-control.js +473 -0
  335. package/lib/vendor/blamejs/lib/cert.js +763 -0
  336. package/lib/vendor/blamejs/lib/chain-writer.js +259 -0
  337. package/lib/vendor/blamejs/lib/circuit-breaker.js +101 -0
  338. package/lib/vendor/blamejs/lib/cli-helpers.js +237 -0
  339. package/lib/vendor/blamejs/lib/cli.js +2328 -0
  340. package/lib/vendor/blamejs/lib/client-hints.js +318 -0
  341. package/lib/vendor/blamejs/lib/cloud-events.js +277 -0
  342. package/lib/vendor/blamejs/lib/cluster-provider-db.js +317 -0
  343. package/lib/vendor/blamejs/lib/cluster-storage.js +351 -0
  344. package/lib/vendor/blamejs/lib/cluster.js +1017 -0
  345. package/lib/vendor/blamejs/lib/cms-codec.js +826 -0
  346. package/lib/vendor/blamejs/lib/codepoint-class.js +262 -0
  347. package/lib/vendor/blamejs/lib/compliance-ai-act-logging.js +190 -0
  348. package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +205 -0
  349. package/lib/vendor/blamejs/lib/compliance-ai-act-risk.js +189 -0
  350. package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +200 -0
  351. package/lib/vendor/blamejs/lib/compliance-ai-act.js +821 -0
  352. package/lib/vendor/blamejs/lib/compliance-eaa.js +204 -0
  353. package/lib/vendor/blamejs/lib/compliance-sanctions-aliases.js +167 -0
  354. package/lib/vendor/blamejs/lib/compliance-sanctions-fetcher.js +206 -0
  355. package/lib/vendor/blamejs/lib/compliance-sanctions-fuzzy.js +297 -0
  356. package/lib/vendor/blamejs/lib/compliance-sanctions.js +569 -0
  357. package/lib/vendor/blamejs/lib/compliance.js +1558 -0
  358. package/lib/vendor/blamejs/lib/config-drift.js +426 -0
  359. package/lib/vendor/blamejs/lib/config.js +446 -0
  360. package/lib/vendor/blamejs/lib/consent.js +369 -0
  361. package/lib/vendor/blamejs/lib/constants.js +209 -0
  362. package/lib/vendor/blamejs/lib/content-credentials.js +704 -0
  363. package/lib/vendor/blamejs/lib/cookies.js +560 -0
  364. package/lib/vendor/blamejs/lib/cra-report.js +299 -0
  365. package/lib/vendor/blamejs/lib/credential-hash.js +394 -0
  366. package/lib/vendor/blamejs/lib/crypto-field.js +1017 -0
  367. package/lib/vendor/blamejs/lib/crypto-hpke-pq.js +187 -0
  368. package/lib/vendor/blamejs/lib/crypto-hpke.js +256 -0
  369. package/lib/vendor/blamejs/lib/crypto.js +1908 -0
  370. package/lib/vendor/blamejs/lib/csp.js +271 -0
  371. package/lib/vendor/blamejs/lib/csv.js +418 -0
  372. package/lib/vendor/blamejs/lib/daemon.js +481 -0
  373. package/lib/vendor/blamejs/lib/dark-patterns.js +488 -0
  374. package/lib/vendor/blamejs/lib/data-act.js +328 -0
  375. package/lib/vendor/blamejs/lib/db-collection.js +587 -0
  376. package/lib/vendor/blamejs/lib/db-declare-row-policy.js +267 -0
  377. package/lib/vendor/blamejs/lib/db-declare-view.js +420 -0
  378. package/lib/vendor/blamejs/lib/db-file-lifecycle.js +333 -0
  379. package/lib/vendor/blamejs/lib/db-query.js +802 -0
  380. package/lib/vendor/blamejs/lib/db-role-context.js +50 -0
  381. package/lib/vendor/blamejs/lib/db-schema.js +322 -0
  382. package/lib/vendor/blamejs/lib/db.js +3111 -0
  383. package/lib/vendor/blamejs/lib/dbsc.js +299 -0
  384. package/lib/vendor/blamejs/lib/ddl-change-control.js +523 -0
  385. package/lib/vendor/blamejs/lib/deprecate.js +377 -0
  386. package/lib/vendor/blamejs/lib/dev.js +405 -0
  387. package/lib/vendor/blamejs/lib/dora.js +402 -0
  388. package/lib/vendor/blamejs/lib/dr-runbook.js +368 -0
  389. package/lib/vendor/blamejs/lib/dsr.js +1188 -0
  390. package/lib/vendor/blamejs/lib/dual-control.js +526 -0
  391. package/lib/vendor/blamejs/lib/early-hints.js +212 -0
  392. package/lib/vendor/blamejs/lib/error-page.js +420 -0
  393. package/lib/vendor/blamejs/lib/events.js +214 -0
  394. package/lib/vendor/blamejs/lib/external-db-migrate.js +659 -0
  395. package/lib/vendor/blamejs/lib/external-db.js +1877 -0
  396. package/lib/vendor/blamejs/lib/fapi2.js +394 -0
  397. package/lib/vendor/blamejs/lib/fda-21cfr11.js +395 -0
  398. package/lib/vendor/blamejs/lib/fdx.js +370 -0
  399. package/lib/vendor/blamejs/lib/fedcm.js +264 -0
  400. package/lib/vendor/blamejs/lib/file-type.js +360 -0
  401. package/lib/vendor/blamejs/lib/file-upload.js +1256 -0
  402. package/lib/vendor/blamejs/lib/flag-cache.js +136 -0
  403. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +135 -0
  404. package/lib/vendor/blamejs/lib/flag-providers.js +279 -0
  405. package/lib/vendor/blamejs/lib/flag-targeting.js +210 -0
  406. package/lib/vendor/blamejs/lib/flag.js +346 -0
  407. package/lib/vendor/blamejs/lib/forms.js +525 -0
  408. package/lib/vendor/blamejs/lib/framework-error.js +724 -0
  409. package/lib/vendor/blamejs/lib/framework-schema.js +845 -0
  410. package/lib/vendor/blamejs/lib/framework-sha1-hibp.js +34 -0
  411. package/lib/vendor/blamejs/lib/fsm.js +469 -0
  412. package/lib/vendor/blamejs/lib/gate-contract.js +1661 -0
  413. package/lib/vendor/blamejs/lib/gdpr-ropa.js +261 -0
  414. package/lib/vendor/blamejs/lib/graphql-federation.js +234 -0
  415. package/lib/vendor/blamejs/lib/guard-agent-registry.js +179 -0
  416. package/lib/vendor/blamejs/lib/guard-all.js +555 -0
  417. package/lib/vendor/blamejs/lib/guard-archive.js +901 -0
  418. package/lib/vendor/blamejs/lib/guard-auth.js +451 -0
  419. package/lib/vendor/blamejs/lib/guard-cidr.js +676 -0
  420. package/lib/vendor/blamejs/lib/guard-csv.js +1176 -0
  421. package/lib/vendor/blamejs/lib/guard-domain.js +814 -0
  422. package/lib/vendor/blamejs/lib/guard-dsn.js +382 -0
  423. package/lib/vendor/blamejs/lib/guard-email.js +951 -0
  424. package/lib/vendor/blamejs/lib/guard-envelope.js +294 -0
  425. package/lib/vendor/blamejs/lib/guard-event-bus-payload.js +217 -0
  426. package/lib/vendor/blamejs/lib/guard-event-bus-topic.js +150 -0
  427. package/lib/vendor/blamejs/lib/guard-filename.js +956 -0
  428. package/lib/vendor/blamejs/lib/guard-graphql.js +731 -0
  429. package/lib/vendor/blamejs/lib/guard-html-wcag-aria.js +164 -0
  430. package/lib/vendor/blamejs/lib/guard-html-wcag-forms.js +144 -0
  431. package/lib/vendor/blamejs/lib/guard-html-wcag-tables.js +154 -0
  432. package/lib/vendor/blamejs/lib/guard-html-wcag-tagwalk.js +44 -0
  433. package/lib/vendor/blamejs/lib/guard-html-wcag.js +470 -0
  434. package/lib/vendor/blamejs/lib/guard-html.js +1209 -0
  435. package/lib/vendor/blamejs/lib/guard-idempotency-key.js +151 -0
  436. package/lib/vendor/blamejs/lib/guard-image.js +584 -0
  437. package/lib/vendor/blamejs/lib/guard-imap-command.js +337 -0
  438. package/lib/vendor/blamejs/lib/guard-jmap.js +321 -0
  439. package/lib/vendor/blamejs/lib/guard-json.js +935 -0
  440. package/lib/vendor/blamejs/lib/guard-jsonpath.js +512 -0
  441. package/lib/vendor/blamejs/lib/guard-jwt.js +772 -0
  442. package/lib/vendor/blamejs/lib/guard-list-id.js +318 -0
  443. package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +412 -0
  444. package/lib/vendor/blamejs/lib/guard-mail-compose.js +282 -0
  445. package/lib/vendor/blamejs/lib/guard-mail-move.js +202 -0
  446. package/lib/vendor/blamejs/lib/guard-mail-query.js +310 -0
  447. package/lib/vendor/blamejs/lib/guard-mail-reply.js +172 -0
  448. package/lib/vendor/blamejs/lib/guard-mail-sieve.js +207 -0
  449. package/lib/vendor/blamejs/lib/guard-managesieve-command.js +566 -0
  450. package/lib/vendor/blamejs/lib/guard-markdown.js +768 -0
  451. package/lib/vendor/blamejs/lib/guard-message-id.js +267 -0
  452. package/lib/vendor/blamejs/lib/guard-mime.js +609 -0
  453. package/lib/vendor/blamejs/lib/guard-oauth.js +650 -0
  454. package/lib/vendor/blamejs/lib/guard-pdf.js +569 -0
  455. package/lib/vendor/blamejs/lib/guard-pop3-command.js +317 -0
  456. package/lib/vendor/blamejs/lib/guard-posture-chain.js +201 -0
  457. package/lib/vendor/blamejs/lib/guard-regex.js +632 -0
  458. package/lib/vendor/blamejs/lib/guard-saga-config.js +157 -0
  459. package/lib/vendor/blamejs/lib/guard-shell.js +522 -0
  460. package/lib/vendor/blamejs/lib/guard-smtp-command.js +594 -0
  461. package/lib/vendor/blamejs/lib/guard-snapshot-envelope.js +168 -0
  462. package/lib/vendor/blamejs/lib/guard-stream-args.js +166 -0
  463. package/lib/vendor/blamejs/lib/guard-svg.js +1163 -0
  464. package/lib/vendor/blamejs/lib/guard-template.js +490 -0
  465. package/lib/vendor/blamejs/lib/guard-tenant-id.js +138 -0
  466. package/lib/vendor/blamejs/lib/guard-time.js +586 -0
  467. package/lib/vendor/blamejs/lib/guard-trace-context.js +172 -0
  468. package/lib/vendor/blamejs/lib/guard-uuid.js +548 -0
  469. package/lib/vendor/blamejs/lib/guard-xml.js +666 -0
  470. package/lib/vendor/blamejs/lib/guard-yaml.js +726 -0
  471. package/lib/vendor/blamejs/lib/hal.js +125 -0
  472. package/lib/vendor/blamejs/lib/handlers.js +350 -0
  473. package/lib/vendor/blamejs/lib/honeytoken.js +168 -0
  474. package/lib/vendor/blamejs/lib/html-balance.js +347 -0
  475. package/lib/vendor/blamejs/lib/http-client-cache.js +923 -0
  476. package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +519 -0
  477. package/lib/vendor/blamejs/lib/http-client.js +2152 -0
  478. package/lib/vendor/blamejs/lib/http-message-signature.js +589 -0
  479. package/lib/vendor/blamejs/lib/http2-teardown.js +34 -0
  480. package/lib/vendor/blamejs/lib/i18n-messageformat.js +398 -0
  481. package/lib/vendor/blamejs/lib/i18n.js +931 -0
  482. package/lib/vendor/blamejs/lib/iab-mspa.js +257 -0
  483. package/lib/vendor/blamejs/lib/iab-tcf.js +461 -0
  484. package/lib/vendor/blamejs/lib/importmap-integrity.js +90 -0
  485. package/lib/vendor/blamejs/lib/inbox.js +435 -0
  486. package/lib/vendor/blamejs/lib/incident-report.js +314 -0
  487. package/lib/vendor/blamejs/lib/ip-utils.js +102 -0
  488. package/lib/vendor/blamejs/lib/jobs.js +185 -0
  489. package/lib/vendor/blamejs/lib/jose-jwe-experimental.js +228 -0
  490. package/lib/vendor/blamejs/lib/jsonapi.js +230 -0
  491. package/lib/vendor/blamejs/lib/keychain.js +865 -0
  492. package/lib/vendor/blamejs/lib/lazy-require.js +48 -0
  493. package/lib/vendor/blamejs/lib/legal-hold.js +374 -0
  494. package/lib/vendor/blamejs/lib/local-db-thin.js +321 -0
  495. package/lib/vendor/blamejs/lib/log-stream-cloudwatch.js +369 -0
  496. package/lib/vendor/blamejs/lib/log-stream-local.js +146 -0
  497. package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +410 -0
  498. package/lib/vendor/blamejs/lib/log-stream-otlp.js +286 -0
  499. package/lib/vendor/blamejs/lib/log-stream-syslog.js +310 -0
  500. package/lib/vendor/blamejs/lib/log-stream-webhook.js +199 -0
  501. package/lib/vendor/blamejs/lib/log-stream.js +584 -0
  502. package/lib/vendor/blamejs/lib/log.js +625 -0
  503. package/lib/vendor/blamejs/lib/lro.js +200 -0
  504. package/lib/vendor/blamejs/lib/mail-agent.js +786 -0
  505. package/lib/vendor/blamejs/lib/mail-arc-sign.js +417 -0
  506. package/lib/vendor/blamejs/lib/mail-arf.js +343 -0
  507. package/lib/vendor/blamejs/lib/mail-auth.js +2144 -0
  508. package/lib/vendor/blamejs/lib/mail-bimi.js +1047 -0
  509. package/lib/vendor/blamejs/lib/mail-bounce.js +955 -0
  510. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +1286 -0
  511. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +789 -0
  512. package/lib/vendor/blamejs/lib/mail-crypto.js +108 -0
  513. package/lib/vendor/blamejs/lib/mail-dav.js +1224 -0
  514. package/lib/vendor/blamejs/lib/mail-deploy.js +1119 -0
  515. package/lib/vendor/blamejs/lib/mail-dkim.js +1250 -0
  516. package/lib/vendor/blamejs/lib/mail-greylist.js +448 -0
  517. package/lib/vendor/blamejs/lib/mail-helo.js +473 -0
  518. package/lib/vendor/blamejs/lib/mail-journal.js +435 -0
  519. package/lib/vendor/blamejs/lib/mail-mdn.js +424 -0
  520. package/lib/vendor/blamejs/lib/mail-rbl.js +392 -0
  521. package/lib/vendor/blamejs/lib/mail-require-tls.js +198 -0
  522. package/lib/vendor/blamejs/lib/mail-scan.js +502 -0
  523. package/lib/vendor/blamejs/lib/mail-send-deliver.js +629 -0
  524. package/lib/vendor/blamejs/lib/mail-server-imap.js +1858 -0
  525. package/lib/vendor/blamejs/lib/mail-server-jmap.js +1565 -0
  526. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +908 -0
  527. package/lib/vendor/blamejs/lib/mail-server-mx.js +969 -0
  528. package/lib/vendor/blamejs/lib/mail-server-pop3.js +915 -0
  529. package/lib/vendor/blamejs/lib/mail-server-rate-limit.js +315 -0
  530. package/lib/vendor/blamejs/lib/mail-server-registry.js +378 -0
  531. package/lib/vendor/blamejs/lib/mail-server-submission.js +1396 -0
  532. package/lib/vendor/blamejs/lib/mail-server-tls.js +445 -0
  533. package/lib/vendor/blamejs/lib/mail-sieve.js +557 -0
  534. package/lib/vendor/blamejs/lib/mail-spam-score.js +284 -0
  535. package/lib/vendor/blamejs/lib/mail-srs.js +248 -0
  536. package/lib/vendor/blamejs/lib/mail-store-fts.js +394 -0
  537. package/lib/vendor/blamejs/lib/mail-store.js +929 -0
  538. package/lib/vendor/blamejs/lib/mail-unsubscribe.js +400 -0
  539. package/lib/vendor/blamejs/lib/mail.js +1971 -0
  540. package/lib/vendor/blamejs/lib/mcp-tool-registry.js +473 -0
  541. package/lib/vendor/blamejs/lib/mcp.js +950 -0
  542. package/lib/vendor/blamejs/lib/metrics.js +1503 -0
  543. package/lib/vendor/blamejs/lib/middleware/age-gate.js +177 -0
  544. package/lib/vendor/blamejs/lib/middleware/ai-act-disclosure.js +203 -0
  545. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +981 -0
  546. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +137 -0
  547. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +171 -0
  548. package/lib/vendor/blamejs/lib/middleware/attach-user.js +220 -0
  549. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +293 -0
  550. package/lib/vendor/blamejs/lib/middleware/body-parser.js +1519 -0
  551. package/lib/vendor/blamejs/lib/middleware/bot-disclose.js +183 -0
  552. package/lib/vendor/blamejs/lib/middleware/bot-guard.js +217 -0
  553. package/lib/vendor/blamejs/lib/middleware/clear-site-data.js +122 -0
  554. package/lib/vendor/blamejs/lib/middleware/compose-pipeline.js +355 -0
  555. package/lib/vendor/blamejs/lib/middleware/compression.js +489 -0
  556. package/lib/vendor/blamejs/lib/middleware/cookies.js +130 -0
  557. package/lib/vendor/blamejs/lib/middleware/cors.js +386 -0
  558. package/lib/vendor/blamejs/lib/middleware/csp-nonce.js +388 -0
  559. package/lib/vendor/blamejs/lib/middleware/csp-report.js +167 -0
  560. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +499 -0
  561. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +243 -0
  562. package/lib/vendor/blamejs/lib/middleware/db-role-for.js +304 -0
  563. package/lib/vendor/blamejs/lib/middleware/dpop.js +402 -0
  564. package/lib/vendor/blamejs/lib/middleware/error-handler.js +69 -0
  565. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +168 -0
  566. package/lib/vendor/blamejs/lib/middleware/flag-context.js +110 -0
  567. package/lib/vendor/blamejs/lib/middleware/gpc.js +153 -0
  568. package/lib/vendor/blamejs/lib/middleware/headers.js +242 -0
  569. package/lib/vendor/blamejs/lib/middleware/health.js +438 -0
  570. package/lib/vendor/blamejs/lib/middleware/host-allowlist.js +189 -0
  571. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +964 -0
  572. package/lib/vendor/blamejs/lib/middleware/index.js +183 -0
  573. package/lib/vendor/blamejs/lib/middleware/nel.js +214 -0
  574. package/lib/vendor/blamejs/lib/middleware/network-allowlist.js +237 -0
  575. package/lib/vendor/blamejs/lib/middleware/no-cache.js +106 -0
  576. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +177 -0
  577. package/lib/vendor/blamejs/lib/middleware/protected-resource-metadata.js +277 -0
  578. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +556 -0
  579. package/lib/vendor/blamejs/lib/middleware/request-id.js +79 -0
  580. package/lib/vendor/blamejs/lib/middleware/request-log.js +205 -0
  581. package/lib/vendor/blamejs/lib/middleware/require-aal.js +138 -0
  582. package/lib/vendor/blamejs/lib/middleware/require-auth.js +144 -0
  583. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +290 -0
  584. package/lib/vendor/blamejs/lib/middleware/require-content-type.js +113 -0
  585. package/lib/vendor/blamejs/lib/middleware/require-methods.js +97 -0
  586. package/lib/vendor/blamejs/lib/middleware/require-mtls.js +212 -0
  587. package/lib/vendor/blamejs/lib/middleware/require-step-up.js +226 -0
  588. package/lib/vendor/blamejs/lib/middleware/scim-server.js +375 -0
  589. package/lib/vendor/blamejs/lib/middleware/security-headers.js +285 -0
  590. package/lib/vendor/blamejs/lib/middleware/security-txt.js +170 -0
  591. package/lib/vendor/blamejs/lib/middleware/span-http-server.js +280 -0
  592. package/lib/vendor/blamejs/lib/middleware/speculation-rules.js +323 -0
  593. package/lib/vendor/blamejs/lib/middleware/sse.js +200 -0
  594. package/lib/vendor/blamejs/lib/middleware/trace-log-correlation.js +167 -0
  595. package/lib/vendor/blamejs/lib/middleware/trace-propagate.js +148 -0
  596. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +749 -0
  597. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +164 -0
  598. package/lib/vendor/blamejs/lib/migration-files.js +37 -0
  599. package/lib/vendor/blamejs/lib/migrations.js +385 -0
  600. package/lib/vendor/blamejs/lib/mime-parse.js +198 -0
  601. package/lib/vendor/blamejs/lib/money.js +699 -0
  602. package/lib/vendor/blamejs/lib/mtls-ca.js +572 -0
  603. package/lib/vendor/blamejs/lib/mtls-engine-default.js +501 -0
  604. package/lib/vendor/blamejs/lib/network-byte-quota.js +308 -0
  605. package/lib/vendor/blamejs/lib/network-dns-resolver.js +533 -0
  606. package/lib/vendor/blamejs/lib/network-dns.js +1930 -0
  607. package/lib/vendor/blamejs/lib/network-heartbeat.js +425 -0
  608. package/lib/vendor/blamejs/lib/network-nts.js +574 -0
  609. package/lib/vendor/blamejs/lib/network-proxy.js +265 -0
  610. package/lib/vendor/blamejs/lib/network-smtp-policy.js +836 -0
  611. package/lib/vendor/blamejs/lib/network-tls.js +3126 -0
  612. package/lib/vendor/blamejs/lib/network.js +346 -0
  613. package/lib/vendor/blamejs/lib/nis2-report.js +181 -0
  614. package/lib/vendor/blamejs/lib/nist-crosswalk.js +293 -0
  615. package/lib/vendor/blamejs/lib/nonce-store.js +177 -0
  616. package/lib/vendor/blamejs/lib/notify.js +683 -0
  617. package/lib/vendor/blamejs/lib/ntp-check.js +458 -0
  618. package/lib/vendor/blamejs/lib/numeric-bounds.js +111 -0
  619. package/lib/vendor/blamejs/lib/numeric-checks.js +40 -0
  620. package/lib/vendor/blamejs/lib/object-store/azure-blob-bucket-ops.js +349 -0
  621. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +488 -0
  622. package/lib/vendor/blamejs/lib/object-store/gcs-bucket-ops.js +351 -0
  623. package/lib/vendor/blamejs/lib/object-store/gcs.js +515 -0
  624. package/lib/vendor/blamejs/lib/object-store/http-put.js +153 -0
  625. package/lib/vendor/blamejs/lib/object-store/http-request.js +38 -0
  626. package/lib/vendor/blamejs/lib/object-store/index.js +197 -0
  627. package/lib/vendor/blamejs/lib/object-store/local.js +163 -0
  628. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +1133 -0
  629. package/lib/vendor/blamejs/lib/object-store/sigv4.js +957 -0
  630. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +420 -0
  631. package/lib/vendor/blamejs/lib/observability-tracer.js +395 -0
  632. package/lib/vendor/blamejs/lib/observability.js +720 -0
  633. package/lib/vendor/blamejs/lib/openapi-paths-builder.js +248 -0
  634. package/lib/vendor/blamejs/lib/openapi-schema-walk.js +192 -0
  635. package/lib/vendor/blamejs/lib/openapi-security.js +169 -0
  636. package/lib/vendor/blamejs/lib/openapi-yaml.js +154 -0
  637. package/lib/vendor/blamejs/lib/openapi.js +489 -0
  638. package/lib/vendor/blamejs/lib/otel-export.js +278 -0
  639. package/lib/vendor/blamejs/lib/outbox.js +547 -0
  640. package/lib/vendor/blamejs/lib/pagination.js +542 -0
  641. package/lib/vendor/blamejs/lib/parsers/index.js +91 -0
  642. package/lib/vendor/blamejs/lib/parsers/safe-env.js +642 -0
  643. package/lib/vendor/blamejs/lib/parsers/safe-ini.js +293 -0
  644. package/lib/vendor/blamejs/lib/parsers/safe-toml.js +784 -0
  645. package/lib/vendor/blamejs/lib/parsers/safe-xml.js +390 -0
  646. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +1015 -0
  647. package/lib/vendor/blamejs/lib/permissions.js +793 -0
  648. package/lib/vendor/blamejs/lib/pick.js +105 -0
  649. package/lib/vendor/blamejs/lib/pqc-agent.js +351 -0
  650. package/lib/vendor/blamejs/lib/pqc-gate.js +279 -0
  651. package/lib/vendor/blamejs/lib/pqc-software.js +271 -0
  652. package/lib/vendor/blamejs/lib/problem-details.js +482 -0
  653. package/lib/vendor/blamejs/lib/process-spawn.js +196 -0
  654. package/lib/vendor/blamejs/lib/promise-pool.js +162 -0
  655. package/lib/vendor/blamejs/lib/protobuf-encoder.js +190 -0
  656. package/lib/vendor/blamejs/lib/protocol-dispatcher.js +161 -0
  657. package/lib/vendor/blamejs/lib/public-suffix.js +403 -0
  658. package/lib/vendor/blamejs/lib/pubsub-cluster.js +154 -0
  659. package/lib/vendor/blamejs/lib/pubsub-redis.js +167 -0
  660. package/lib/vendor/blamejs/lib/pubsub.js +463 -0
  661. package/lib/vendor/blamejs/lib/queue-local.js +476 -0
  662. package/lib/vendor/blamejs/lib/queue-redis.js +745 -0
  663. package/lib/vendor/blamejs/lib/queue-sqs.js +319 -0
  664. package/lib/vendor/blamejs/lib/queue.js +1016 -0
  665. package/lib/vendor/blamejs/lib/redact.js +1007 -0
  666. package/lib/vendor/blamejs/lib/redis-client.js +520 -0
  667. package/lib/vendor/blamejs/lib/render.js +285 -0
  668. package/lib/vendor/blamejs/lib/request-helpers.js +767 -0
  669. package/lib/vendor/blamejs/lib/resource-access-lock.js +116 -0
  670. package/lib/vendor/blamejs/lib/restore-bundle.js +340 -0
  671. package/lib/vendor/blamejs/lib/restore-rollback.js +365 -0
  672. package/lib/vendor/blamejs/lib/restore.js +409 -0
  673. package/lib/vendor/blamejs/lib/retention.js +640 -0
  674. package/lib/vendor/blamejs/lib/retry.js +523 -0
  675. package/lib/vendor/blamejs/lib/router.js +1289 -0
  676. package/lib/vendor/blamejs/lib/safe-async.js +1184 -0
  677. package/lib/vendor/blamejs/lib/safe-buffer.js +562 -0
  678. package/lib/vendor/blamejs/lib/safe-decompress.js +297 -0
  679. package/lib/vendor/blamejs/lib/safe-dns.js +665 -0
  680. package/lib/vendor/blamejs/lib/safe-ical.js +634 -0
  681. package/lib/vendor/blamejs/lib/safe-icap.js +502 -0
  682. package/lib/vendor/blamejs/lib/safe-json.js +946 -0
  683. package/lib/vendor/blamejs/lib/safe-jsonpath.js +285 -0
  684. package/lib/vendor/blamejs/lib/safe-mime.js +831 -0
  685. package/lib/vendor/blamejs/lib/safe-mount-info.js +306 -0
  686. package/lib/vendor/blamejs/lib/safe-path.js +254 -0
  687. package/lib/vendor/blamejs/lib/safe-redirect.js +106 -0
  688. package/lib/vendor/blamejs/lib/safe-schema.js +1810 -0
  689. package/lib/vendor/blamejs/lib/safe-sieve.js +684 -0
  690. package/lib/vendor/blamejs/lib/safe-smtp.js +185 -0
  691. package/lib/vendor/blamejs/lib/safe-sql.js +363 -0
  692. package/lib/vendor/blamejs/lib/safe-url.js +428 -0
  693. package/lib/vendor/blamejs/lib/safe-vcard.js +473 -0
  694. package/lib/vendor/blamejs/lib/sandbox-worker.js +135 -0
  695. package/lib/vendor/blamejs/lib/sandbox.js +358 -0
  696. package/lib/vendor/blamejs/lib/scheduler.js +827 -0
  697. package/lib/vendor/blamejs/lib/sd-notify.js +269 -0
  698. package/lib/vendor/blamejs/lib/sec-cyber.js +214 -0
  699. package/lib/vendor/blamejs/lib/security-assert.js +395 -0
  700. package/lib/vendor/blamejs/lib/seeders.js +620 -0
  701. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +309 -0
  702. package/lib/vendor/blamejs/lib/self-update.js +804 -0
  703. package/lib/vendor/blamejs/lib/server-timing.js +174 -0
  704. package/lib/vendor/blamejs/lib/session-device-binding.js +431 -0
  705. package/lib/vendor/blamejs/lib/session-stores.js +138 -0
  706. package/lib/vendor/blamejs/lib/session.js +1162 -0
  707. package/lib/vendor/blamejs/lib/slug.js +381 -0
  708. package/lib/vendor/blamejs/lib/sse.js +349 -0
  709. package/lib/vendor/blamejs/lib/ssrf-guard.js +792 -0
  710. package/lib/vendor/blamejs/lib/standard-webhooks.js +183 -0
  711. package/lib/vendor/blamejs/lib/static.js +1249 -0
  712. package/lib/vendor/blamejs/lib/storage.js +1272 -0
  713. package/lib/vendor/blamejs/lib/stream-throttle.js +235 -0
  714. package/lib/vendor/blamejs/lib/structured-fields.js +244 -0
  715. package/lib/vendor/blamejs/lib/subject.js +667 -0
  716. package/lib/vendor/blamejs/lib/tcpa-10dlc.js +175 -0
  717. package/lib/vendor/blamejs/lib/template.js +931 -0
  718. package/lib/vendor/blamejs/lib/tenant-quota.js +545 -0
  719. package/lib/vendor/blamejs/lib/test-harness.js +275 -0
  720. package/lib/vendor/blamejs/lib/testing.js +1185 -0
  721. package/lib/vendor/blamejs/lib/time.js +578 -0
  722. package/lib/vendor/blamejs/lib/tls-exporter.js +239 -0
  723. package/lib/vendor/blamejs/lib/totp.js +318 -0
  724. package/lib/vendor/blamejs/lib/tracing.js +546 -0
  725. package/lib/vendor/blamejs/lib/uuid.js +207 -0
  726. package/lib/vendor/blamejs/lib/validate-opts.js +381 -0
  727. package/lib/vendor/blamejs/lib/vault/index.js +638 -0
  728. package/lib/vendor/blamejs/lib/vault/passphrase-ops.js +311 -0
  729. package/lib/vendor/blamejs/lib/vault/passphrase-source.js +198 -0
  730. package/lib/vendor/blamejs/lib/vault/rotate.js +803 -0
  731. package/lib/vendor/blamejs/lib/vault/seal-pem-file.js +471 -0
  732. package/lib/vendor/blamejs/lib/vault/wrap.js +296 -0
  733. package/lib/vendor/blamejs/lib/vault-aad.js +259 -0
  734. package/lib/vendor/blamejs/lib/vendor/.vendor-data-pubkey +4 -0
  735. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +161 -0
  736. package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.data.js +68 -0
  737. package/lib/vendor/blamejs/lib/vendor/bimi-trust-anchors.pem +33 -0
  738. package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.data.js +1325 -0
  739. package/lib/vendor/blamejs/lib/vendor/common-passwords-top-10000.txt +10002 -0
  740. package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +9 -0
  741. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +18 -0
  742. package/lib/vendor/blamejs/lib/vendor/pki.cjs +181 -0
  743. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +16382 -0
  744. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5881 -0
  745. package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +328 -0
  746. package/lib/vendor/blamejs/lib/vendor/vendor-data-pubkey.js +16 -0
  747. package/lib/vendor/blamejs/lib/vendor-data.js +520 -0
  748. package/lib/vendor/blamejs/lib/vex.js +630 -0
  749. package/lib/vendor/blamejs/lib/watcher.js +608 -0
  750. package/lib/vendor/blamejs/lib/web-push-vapid.js +322 -0
  751. package/lib/vendor/blamejs/lib/webhook.js +977 -0
  752. package/lib/vendor/blamejs/lib/websocket-channels.js +327 -0
  753. package/lib/vendor/blamejs/lib/websocket.js +1561 -0
  754. package/lib/vendor/blamejs/lib/wiki-concepts.js +338 -0
  755. package/lib/vendor/blamejs/lib/worker-pool.js +464 -0
  756. package/lib/vendor/blamejs/lib/ws-client.js +978 -0
  757. package/lib/vendor/blamejs/lib/xml-c14n.js +506 -0
  758. package/lib/vendor/blamejs/memory/specs/node-26-map-getorinsert-migration.md +164 -0
  759. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +19 -0
  760. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/README.md +88 -0
  761. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/build.sh +26 -0
  762. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/project.yaml +28 -0
  763. package/lib/vendor/blamejs/package.json +81 -0
  764. package/lib/vendor/blamejs/release-notes/v0.0.x.json +310 -0
  765. package/lib/vendor/blamejs/release-notes/v0.1.x.json +1798 -0
  766. package/lib/vendor/blamejs/release-notes/v0.10.x.json +1288 -0
  767. package/lib/vendor/blamejs/release-notes/v0.11.x.json +2551 -0
  768. package/lib/vendor/blamejs/release-notes/v0.12.0.json +64 -0
  769. package/lib/vendor/blamejs/release-notes/v0.12.1.json +32 -0
  770. package/lib/vendor/blamejs/release-notes/v0.12.2.json +45 -0
  771. package/lib/vendor/blamejs/release-notes/v0.2.x.json +706 -0
  772. package/lib/vendor/blamejs/release-notes/v0.3.x.json +786 -0
  773. package/lib/vendor/blamejs/release-notes/v0.4.x.json +588 -0
  774. package/lib/vendor/blamejs/release-notes/v0.5.x.json +390 -0
  775. package/lib/vendor/blamejs/release-notes/v0.6.x.json +1947 -0
  776. package/lib/vendor/blamejs/release-notes/v0.7.x.json +3811 -0
  777. package/lib/vendor/blamejs/release-notes/v0.8.x.json +3318 -0
  778. package/lib/vendor/blamejs/release-notes/v0.9.x.json +2257 -0
  779. package/lib/vendor/blamejs/scripts/build-vendored-sbom.js +325 -0
  780. package/lib/vendor/blamejs/scripts/check-api-snapshot.js +62 -0
  781. package/lib/vendor/blamejs/scripts/check-changelog-extract.js +108 -0
  782. package/lib/vendor/blamejs/scripts/check-pack-against-gitignore.js +83 -0
  783. package/lib/vendor/blamejs/scripts/check-services.js +483 -0
  784. package/lib/vendor/blamejs/scripts/check-vendor-currency.js +349 -0
  785. package/lib/vendor/blamejs/scripts/consolidate-release-notes.js +216 -0
  786. package/lib/vendor/blamejs/scripts/gen-migrating.js +275 -0
  787. package/lib/vendor/blamejs/scripts/generate-changelog-entry.js +577 -0
  788. package/lib/vendor/blamejs/scripts/generate-release-signing-key.js +79 -0
  789. package/lib/vendor/blamejs/scripts/publish-dep-confusion-placeholder.sh +101 -0
  790. package/lib/vendor/blamejs/scripts/refresh-api-snapshot.js +31 -0
  791. package/lib/vendor/blamejs/scripts/refresh-vendor-manifest.js +132 -0
  792. package/lib/vendor/blamejs/scripts/release.js +652 -0
  793. package/lib/vendor/blamejs/scripts/sha3-digest.js +62 -0
  794. package/lib/vendor/blamejs/scripts/sign-release-artifact.js +92 -0
  795. package/lib/vendor/blamejs/scripts/test-integration.js +181 -0
  796. package/lib/vendor/blamejs/scripts/test-wiki-integration.js +126 -0
  797. package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +77 -0
  798. package/lib/vendor/blamejs/scripts/vendor-data-gen.js +186 -0
  799. package/lib/vendor/blamejs/scripts/vendor-data-keygen.js +101 -0
  800. package/lib/vendor/blamejs/scripts/vendor-update.sh +278 -0
  801. package/lib/vendor/blamejs/test/00-primitives.js +19075 -0
  802. package/lib/vendor/blamejs/test/10-state.js +622 -0
  803. package/lib/vendor/blamejs/test/20-db.js +561 -0
  804. package/lib/vendor/blamejs/test/30-chain.js +2110 -0
  805. package/lib/vendor/blamejs/test/40-consumers.js +2453 -0
  806. package/lib/vendor/blamejs/test/50-integration.js +486 -0
  807. package/lib/vendor/blamejs/test/_helpers.js +10 -0
  808. package/lib/vendor/blamejs/test/_smoke-worker.js +69 -0
  809. package/lib/vendor/blamejs/test/fixtures/exploit-corpus/corpus.json +368 -0
  810. package/lib/vendor/blamejs/test/fixtures/http-client-stream-payload.txt +2 -0
  811. package/lib/vendor/blamejs/test/fixtures/worker-pool/echo.js +52 -0
  812. package/lib/vendor/blamejs/test/helpers/_codebase-shingle-worker.js +24 -0
  813. package/lib/vendor/blamejs/test/helpers/_codebase-shingle.js +203 -0
  814. package/lib/vendor/blamejs/test/helpers/_shape-match.js +513 -0
  815. package/lib/vendor/blamejs/test/helpers/check.js +36 -0
  816. package/lib/vendor/blamejs/test/helpers/cluster.js +70 -0
  817. package/lib/vendor/blamejs/test/helpers/db.js +143 -0
  818. package/lib/vendor/blamejs/test/helpers/drivers.js +207 -0
  819. package/lib/vendor/blamejs/test/helpers/fs-watch.js +101 -0
  820. package/lib/vendor/blamejs/test/helpers/http.js +14 -0
  821. package/lib/vendor/blamejs/test/helpers/index.js +93 -0
  822. package/lib/vendor/blamejs/test/helpers/json-round-trip.js +120 -0
  823. package/lib/vendor/blamejs/test/helpers/mocks.js +20 -0
  824. package/lib/vendor/blamejs/test/helpers/otel.js +13 -0
  825. package/lib/vendor/blamejs/test/helpers/services.js +380 -0
  826. package/lib/vendor/blamejs/test/helpers/wait.js +206 -0
  827. package/lib/vendor/blamejs/test/integration/cache.test.js +235 -0
  828. package/lib/vendor/blamejs/test/integration/cluster-provider-mysql.test.js +174 -0
  829. package/lib/vendor/blamejs/test/integration/federation-auth.test.js +611 -0
  830. package/lib/vendor/blamejs/test/integration/http-client.test.js +129 -0
  831. package/lib/vendor/blamejs/test/integration/log-stream.test.js +219 -0
  832. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +181 -0
  833. package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +152 -0
  834. package/lib/vendor/blamejs/test/integration/mail-smtp.test.js +161 -0
  835. package/lib/vendor/blamejs/test/integration/mtls-ca.test.js +289 -0
  836. package/lib/vendor/blamejs/test/integration/network-dns.test.js +123 -0
  837. package/lib/vendor/blamejs/test/integration/network-heartbeat.test.js +101 -0
  838. package/lib/vendor/blamejs/test/integration/ntp-check.test.js +89 -0
  839. package/lib/vendor/blamejs/test/integration/object-store-sigv4.test.js +403 -0
  840. package/lib/vendor/blamejs/test/integration/pqc-pkcs8-forward-compat.test.js +271 -0
  841. package/lib/vendor/blamejs/test/integration/pubsub.test.js +137 -0
  842. package/lib/vendor/blamejs/test/integration/queue-redis.test.js +352 -0
  843. package/lib/vendor/blamejs/test/integration/redis-client-tls.test.js +96 -0
  844. package/lib/vendor/blamejs/test/integration/ssrf-guard.test.js +98 -0
  845. package/lib/vendor/blamejs/test/integration/websocket-permessage-deflate.test.js +261 -0
  846. package/lib/vendor/blamejs/test/integration/ws-client-roundtrip.test.js +230 -0
  847. package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +211 -0
  848. package/lib/vendor/blamejs/test/layer-0-primitives/a2a.test.js +59 -0
  849. package/lib/vendor/blamejs/test/layer-0-primitives/access-lock.test.js +136 -0
  850. package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +219 -0
  851. package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +69 -0
  852. package/lib/vendor/blamejs/test/layer-0-primitives/agent-event-bus.test.js +266 -0
  853. package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +262 -0
  854. package/lib/vendor/blamejs/test/layer-0-primitives/agent-orchestrator.test.js +390 -0
  855. package/lib/vendor/blamejs/test/layer-0-primitives/agent-posture-chain.test.js +174 -0
  856. package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +279 -0
  857. package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +322 -0
  858. package/lib/vendor/blamejs/test/layer-0-primitives/agent-stream.test.js +227 -0
  859. package/lib/vendor/blamejs/test/layer-0-primitives/agent-tenant.test.js +302 -0
  860. package/lib/vendor/blamejs/test/layer-0-primitives/agent-trace.test.js +150 -0
  861. package/lib/vendor/blamejs/test/layer-0-primitives/ai-adverse-decision.test.js +44 -0
  862. package/lib/vendor/blamejs/test/layer-0-primitives/ai-content-detect.test.js +150 -0
  863. package/lib/vendor/blamejs/test/layer-0-primitives/ai-input.test.js +50 -0
  864. package/lib/vendor/blamejs/test/layer-0-primitives/ai-model-manifest.test.js +96 -0
  865. package/lib/vendor/blamejs/test/layer-0-primitives/ai-pref.test.js +76 -0
  866. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +1080 -0
  867. package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +311 -0
  868. package/lib/vendor/blamejs/test/layer-0-primitives/archive-zip-stream.test.js +291 -0
  869. package/lib/vendor/blamejs/test/layer-0-primitives/archive.test.js +140 -0
  870. package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +267 -0
  871. package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +108 -0
  872. package/lib/vendor/blamejs/test/layer-0-primitives/asyncapi.test.js +929 -0
  873. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-conflict-path.test.js +80 -0
  874. package/lib/vendor/blamejs/test/layer-0-primitives/audit-cve-defensive.test.js +176 -0
  875. package/lib/vendor/blamejs/test/layer-0-primitives/audit-daily-review.test.js +132 -0
  876. package/lib/vendor/blamejs/test/layer-0-primitives/audit-export-cadf.test.js +97 -0
  877. package/lib/vendor/blamejs/test/layer-0-primitives/audit-framework-namespaces.test.js +141 -0
  878. package/lib/vendor/blamejs/test/layer-0-primitives/audit-segregation.test.js +115 -0
  879. package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-ml-dsa-65.test.js +163 -0
  880. package/lib/vendor/blamejs/test/layer-0-primitives/audit-use-store.test.js +246 -0
  881. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge-verifier.test.js +485 -0
  882. package/lib/vendor/blamejs/test/layer-0-primitives/auth-bot-challenge.test.js +331 -0
  883. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +352 -0
  884. package/lib/vendor/blamejs/test/layer-0-primitives/auth-lockout.test.js +572 -0
  885. package/lib/vendor/blamejs/test/layer-0-primitives/auth-password-audit.test.js +61 -0
  886. package/lib/vendor/blamejs/test/layer-0-primitives/azure-blob-bucket-ops.test.js +258 -0
  887. package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +105 -0
  888. package/lib/vendor/blamejs/test/layer-0-primitives/backup-worker.test.js +34 -0
  889. package/lib/vendor/blamejs/test/layer-0-primitives/bearer-auth.test.js +107 -0
  890. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-chunked-malformed.test.js +131 -0
  891. package/lib/vendor/blamejs/test/layer-0-primitives/body-parser-smuggling.test.js +118 -0
  892. package/lib/vendor/blamejs/test/layer-0-primitives/boot-gates.test.js +85 -0
  893. package/lib/vendor/blamejs/test/layer-0-primitives/breach-deadline.test.js +38 -0
  894. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +861 -0
  895. package/lib/vendor/blamejs/test/layer-0-primitives/budr.test.js +55 -0
  896. package/lib/vendor/blamejs/test/layer-0-primitives/bundler-engine.test.js +209 -0
  897. package/lib/vendor/blamejs/test/layer-0-primitives/cache-status.test.js +129 -0
  898. package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +871 -0
  899. package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +891 -0
  900. package/lib/vendor/blamejs/test/layer-0-primitives/canonical-json-jcs.test.js +43 -0
  901. package/lib/vendor/blamejs/test/layer-0-primitives/cdn-cache-control.test.js +243 -0
  902. package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +550 -0
  903. package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +107 -0
  904. package/lib/vendor/blamejs/test/layer-0-primitives/cli-api-key.test.js +147 -0
  905. package/lib/vendor/blamejs/test/layer-0-primitives/cli-audit-verify-chain.test.js +104 -0
  906. package/lib/vendor/blamejs/test/layer-0-primitives/cli-backup.test.js +135 -0
  907. package/lib/vendor/blamejs/test/layer-0-primitives/cli-config-drift.test.js +67 -0
  908. package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +75 -0
  909. package/lib/vendor/blamejs/test/layer-0-primitives/cli-file-type.test.js +98 -0
  910. package/lib/vendor/blamejs/test/layer-0-primitives/cli-helpers.test.js +145 -0
  911. package/lib/vendor/blamejs/test/layer-0-primitives/cli-mtls.test.js +133 -0
  912. package/lib/vendor/blamejs/test/layer-0-primitives/cli-password.test.js +97 -0
  913. package/lib/vendor/blamejs/test/layer-0-primitives/cli-restore.test.js +160 -0
  914. package/lib/vendor/blamejs/test/layer-0-primitives/cli-retention.test.js +84 -0
  915. package/lib/vendor/blamejs/test/layer-0-primitives/cli-security.test.js +69 -0
  916. package/lib/vendor/blamejs/test/layer-0-primitives/cli-vault.test.js +142 -0
  917. package/lib/vendor/blamejs/test/layer-0-primitives/client-hints.test.js +133 -0
  918. package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +237 -0
  919. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +9600 -0
  920. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +575 -0
  921. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-cascade.test.js +89 -0
  922. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-eaa.test.js +36 -0
  923. package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +712 -0
  924. package/lib/vendor/blamejs/test/layer-0-primitives/compliance.test.js +278 -0
  925. package/lib/vendor/blamejs/test/layer-0-primitives/config-drift.test.js +97 -0
  926. package/lib/vendor/blamejs/test/layer-0-primitives/config.test.js +424 -0
  927. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +94 -0
  928. package/lib/vendor/blamejs/test/layer-0-primitives/cors.test.js +357 -0
  929. package/lib/vendor/blamejs/test/layer-0-primitives/cra-report.test.js +31 -0
  930. package/lib/vendor/blamejs/test/layer-0-primitives/credential-hash.test.js +226 -0
  931. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-base64url.test.js +86 -0
  932. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-envelope.test.js +85 -0
  933. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +193 -0
  934. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +98 -0
  935. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke-pq.test.js +132 -0
  936. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hpke.test.js +155 -0
  937. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-mlkem768-x25519.test.js +129 -0
  938. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-namespace-hash.test.js +0 -0
  939. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-random-int.test.js +72 -0
  940. package/lib/vendor/blamejs/test/layer-0-primitives/csp-builder.test.js +96 -0
  941. package/lib/vendor/blamejs/test/layer-0-primitives/csp-nonce.test.js +401 -0
  942. package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +34 -0
  943. package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +180 -0
  944. package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +210 -0
  945. package/lib/vendor/blamejs/test/layer-0-primitives/daily-byte-quota.test.js +153 -0
  946. package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +66 -0
  947. package/lib/vendor/blamejs/test/layer-0-primitives/data-act.test.js +74 -0
  948. package/lib/vendor/blamejs/test/layer-0-primitives/db-collection-extensions.test.js +226 -0
  949. package/lib/vendor/blamejs/test/layer-0-primitives/db-collection.test.js +136 -0
  950. package/lib/vendor/blamejs/test/layer-0-primitives/db-init-extensions.test.js +165 -0
  951. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-cross-schema.test.js +150 -0
  952. package/lib/vendor/blamejs/test/layer-0-primitives/db-query-extensions.test.js +191 -0
  953. package/lib/vendor/blamejs/test/layer-0-primitives/db-role-for.test.js +228 -0
  954. package/lib/vendor/blamejs/test/layer-0-primitives/db-vacuum.test.js +55 -0
  955. package/lib/vendor/blamejs/test/layer-0-primitives/db-worm.test.js +89 -0
  956. package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +184 -0
  957. package/lib/vendor/blamejs/test/layer-0-primitives/declare-row-policy.test.js +203 -0
  958. package/lib/vendor/blamejs/test/layer-0-primitives/declare-view.test.js +303 -0
  959. package/lib/vendor/blamejs/test/layer-0-primitives/dns-dnssec-algorithm.test.js +163 -0
  960. package/lib/vendor/blamejs/test/layer-0-primitives/dns-null-mx.test.js +39 -0
  961. package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +165 -0
  962. package/lib/vendor/blamejs/test/layer-0-primitives/dr-runbook.test.js +59 -0
  963. package/lib/vendor/blamejs/test/layer-0-primitives/dsr-state-rules.test.js +55 -0
  964. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +786 -0
  965. package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +105 -0
  966. package/lib/vendor/blamejs/test/layer-0-primitives/early-hints.test.js +147 -0
  967. package/lib/vendor/blamejs/test/layer-0-primitives/events.test.js +105 -0
  968. package/lib/vendor/blamejs/test/layer-0-primitives/exploit-replay.test.js +243 -0
  969. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-hardening.test.js +181 -0
  970. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +190 -0
  971. package/lib/vendor/blamejs/test/layer-0-primitives/external-db-routing.test.js +531 -0
  972. package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +118 -0
  973. package/lib/vendor/blamejs/test/layer-0-primitives/fapi2.test.js +89 -0
  974. package/lib/vendor/blamejs/test/layer-0-primitives/fda-21cfr11.test.js +156 -0
  975. package/lib/vendor/blamejs/test/layer-0-primitives/fdx.test.js +79 -0
  976. package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +216 -0
  977. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +434 -0
  978. package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +432 -0
  979. package/lib/vendor/blamejs/test/layer-0-primitives/file-type.test.js +81 -0
  980. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +887 -0
  981. package/lib/vendor/blamejs/test/layer-0-primitives/forensic-snapshot.test.js +51 -0
  982. package/lib/vendor/blamejs/test/layer-0-primitives/fsm.test.js +375 -0
  983. package/lib/vendor/blamejs/test/layer-0-primitives/gcs-bucket-ops.test.js +321 -0
  984. package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +41 -0
  985. package/lib/vendor/blamejs/test/layer-0-primitives/graphql-federation.test.js +32 -0
  986. package/lib/vendor/blamejs/test/layer-0-primitives/guard-agent-registry.test.js +87 -0
  987. package/lib/vendor/blamejs/test/layer-0-primitives/guard-all.test.js +328 -0
  988. package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +339 -0
  989. package/lib/vendor/blamejs/test/layer-0-primitives/guard-csv.test.js +694 -0
  990. package/lib/vendor/blamejs/test/layer-0-primitives/guard-dsn.test.js +296 -0
  991. package/lib/vendor/blamejs/test/layer-0-primitives/guard-email.test.js +234 -0
  992. package/lib/vendor/blamejs/test/layer-0-primitives/guard-envelope.test.js +192 -0
  993. package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-payload.test.js +89 -0
  994. package/lib/vendor/blamejs/test/layer-0-primitives/guard-event-bus-topic.test.js +71 -0
  995. package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +386 -0
  996. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html-wcag.test.js +859 -0
  997. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +357 -0
  998. package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +92 -0
  999. package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
  1000. package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +174 -0
  1001. package/lib/vendor/blamejs/test/layer-0-primitives/guard-json.test.js +317 -0
  1002. package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-id.test.js +199 -0
  1003. package/lib/vendor/blamejs/test/layer-0-primitives/guard-list-unsubscribe.test.js +214 -0
  1004. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-compose.test.js +111 -0
  1005. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-move.test.js +110 -0
  1006. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-query.test.js +112 -0
  1007. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-reply.test.js +86 -0
  1008. package/lib/vendor/blamejs/test/layer-0-primitives/guard-mail-sieve.test.js +92 -0
  1009. package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +301 -0
  1010. package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +265 -0
  1011. package/lib/vendor/blamejs/test/layer-0-primitives/guard-message-id.test.js +0 -0
  1012. package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +161 -0
  1013. package/lib/vendor/blamejs/test/layer-0-primitives/guard-posture-chain.test.js +100 -0
  1014. package/lib/vendor/blamejs/test/layer-0-primitives/guard-saga-config.test.js +79 -0
  1015. package/lib/vendor/blamejs/test/layer-0-primitives/guard-smtp-command.test.js +269 -0
  1016. package/lib/vendor/blamejs/test/layer-0-primitives/guard-snapshot-envelope.test.js +89 -0
  1017. package/lib/vendor/blamejs/test/layer-0-primitives/guard-stream-args.test.js +78 -0
  1018. package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +288 -0
  1019. package/lib/vendor/blamejs/test/layer-0-primitives/guard-tenant-id.test.js +69 -0
  1020. package/lib/vendor/blamejs/test/layer-0-primitives/guard-trace-context.test.js +102 -0
  1021. package/lib/vendor/blamejs/test/layer-0-primitives/guard-xml.test.js +202 -0
  1022. package/lib/vendor/blamejs/test/layer-0-primitives/guard-yaml.test.js +203 -0
  1023. package/lib/vendor/blamejs/test/layer-0-primitives/hal.test.js +51 -0
  1024. package/lib/vendor/blamejs/test/layer-0-primitives/honeytoken.test.js +50 -0
  1025. package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +37 -0
  1026. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +692 -0
  1027. package/lib/vendor/blamejs/test/layer-0-primitives/http-client-stream.test.js +280 -0
  1028. package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +225 -0
  1029. package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +203 -0
  1030. package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +991 -0
  1031. package/lib/vendor/blamejs/test/layer-0-primitives/iab-mspa.test.js +63 -0
  1032. package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +73 -0
  1033. package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +612 -0
  1034. package/lib/vendor/blamejs/test/layer-0-primitives/importmap-integrity.test.js +56 -0
  1035. package/lib/vendor/blamejs/test/layer-0-primitives/inbox.test.js +166 -0
  1036. package/lib/vendor/blamejs/test/layer-0-primitives/incident-report.test.js +29 -0
  1037. package/lib/vendor/blamejs/test/layer-0-primitives/jose-jwe-experimental.test.js +121 -0
  1038. package/lib/vendor/blamejs/test/layer-0-primitives/json-api.test.js +58 -0
  1039. package/lib/vendor/blamejs/test/layer-0-primitives/json-round-trip-helper.test.js +110 -0
  1040. package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +159 -0
  1041. package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
  1042. package/lib/vendor/blamejs/test/layer-0-primitives/legal-hold.test.js +118 -0
  1043. package/lib/vendor/blamejs/test/layer-0-primitives/local-db-thin.test.js +150 -0
  1044. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-cloudwatch.test.js +489 -0
  1045. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp-grpc.test.js +207 -0
  1046. package/lib/vendor/blamejs/test/layer-0-primitives/log-stream-otlp.test.js +283 -0
  1047. package/lib/vendor/blamejs/test/layer-0-primitives/lro.test.js +65 -0
  1048. package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +417 -0
  1049. package/lib/vendor/blamejs/test/layer-0-primitives/mail-arf.test.js +208 -0
  1050. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +910 -0
  1051. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +502 -0
  1052. package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +680 -0
  1053. package/lib/vendor/blamejs/test/layer-0-primitives/mail-canspam.test.js +128 -0
  1054. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +149 -0
  1055. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +323 -0
  1056. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +297 -0
  1057. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +514 -0
  1058. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy-tlsrpt.test.js +369 -0
  1059. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +199 -0
  1060. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +627 -0
  1061. package/lib/vendor/blamejs/test/layer-0-primitives/mail-feedback-id.test.js +56 -0
  1062. package/lib/vendor/blamejs/test/layer-0-primitives/mail-greylist.test.js +217 -0
  1063. package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +283 -0
  1064. package/lib/vendor/blamejs/test/layer-0-primitives/mail-journal.test.js +217 -0
  1065. package/lib/vendor/blamejs/test/layer-0-primitives/mail-mdn.test.js +334 -0
  1066. package/lib/vendor/blamejs/test/layer-0-primitives/mail-rbl.test.js +271 -0
  1067. package/lib/vendor/blamejs/test/layer-0-primitives/mail-require-tls.test.js +128 -0
  1068. package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +215 -0
  1069. package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +336 -0
  1070. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +732 -0
  1071. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +840 -0
  1072. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +130 -0
  1073. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +285 -0
  1074. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +74 -0
  1075. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +112 -0
  1076. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +229 -0
  1077. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +394 -0
  1078. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-tls.test.js +147 -0
  1079. package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +151 -0
  1080. package/lib/vendor/blamejs/test/layer-0-primitives/mail-spam-score.test.js +204 -0
  1081. package/lib/vendor/blamejs/test/layer-0-primitives/mail-srs.test.js +152 -0
  1082. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +279 -0
  1083. package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +323 -0
  1084. package/lib/vendor/blamejs/test/layer-0-primitives/mail-unsubscribe.test.js +165 -0
  1085. package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +439 -0
  1086. package/lib/vendor/blamejs/test/layer-0-primitives/mcp-tool-registry.test.js +202 -0
  1087. package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +155 -0
  1088. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-shadow-registry.test.js +112 -0
  1089. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +224 -0
  1090. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-compose-pipeline.test.js +278 -0
  1091. package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +376 -0
  1092. package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-paths.test.js +89 -0
  1093. package/lib/vendor/blamejs/test/layer-0-primitives/nel.test.js +200 -0
  1094. package/lib/vendor/blamejs/test/layer-0-primitives/network-allowlist.test.js +106 -0
  1095. package/lib/vendor/blamejs/test/layer-0-primitives/network-byte-quota.test.js +133 -0
  1096. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +372 -0
  1097. package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +635 -0
  1098. package/lib/vendor/blamejs/test/layer-0-primitives/network-heartbeat-passive.test.js +128 -0
  1099. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-build-options.test.js +130 -0
  1100. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls-ct-inclusion.test.js +179 -0
  1101. package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +447 -0
  1102. package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +369 -0
  1103. package/lib/vendor/blamejs/test/layer-0-primitives/nis2-report.test.js +21 -0
  1104. package/lib/vendor/blamejs/test/layer-0-primitives/nist-crosswalk.test.js +42 -0
  1105. package/lib/vendor/blamejs/test/layer-0-primitives/no-cache.test.js +98 -0
  1106. package/lib/vendor/blamejs/test/layer-0-primitives/notify.test.js +707 -0
  1107. package/lib/vendor/blamejs/test/layer-0-primitives/numeric-bounds.test.js +142 -0
  1108. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +72 -0
  1109. package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +597 -0
  1110. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +190 -0
  1111. package/lib/vendor/blamejs/test/layer-0-primitives/openapi.test.js +877 -0
  1112. package/lib/vendor/blamejs/test/layer-0-primitives/otel-export.test.js +257 -0
  1113. package/lib/vendor/blamejs/test/layer-0-primitives/pagination.test.js +522 -0
  1114. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +216 -0
  1115. package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +324 -0
  1116. package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +546 -0
  1117. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +153 -0
  1118. package/lib/vendor/blamejs/test/layer-0-primitives/pqc-software.test.js +94 -0
  1119. package/lib/vendor/blamejs/test/layer-0-primitives/problem-details.test.js +195 -0
  1120. package/lib/vendor/blamejs/test/layer-0-primitives/process-spawn.test.js +62 -0
  1121. package/lib/vendor/blamejs/test/layer-0-primitives/promise-pool.test.js +93 -0
  1122. package/lib/vendor/blamejs/test/layer-0-primitives/protected-resource-metadata.test.js +68 -0
  1123. package/lib/vendor/blamejs/test/layer-0-primitives/protobuf-encoder.test.js +138 -0
  1124. package/lib/vendor/blamejs/test/layer-0-primitives/protocol-dispatcher.test.js +174 -0
  1125. package/lib/vendor/blamejs/test/layer-0-primitives/public-suffix.test.js +197 -0
  1126. package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +232 -0
  1127. package/lib/vendor/blamejs/test/layer-0-primitives/queue-dlq-extend-lease.test.js +178 -0
  1128. package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +322 -0
  1129. package/lib/vendor/blamejs/test/layer-0-primitives/queue-priority-rate-progress.test.js +266 -0
  1130. package/lib/vendor/blamejs/test/layer-0-primitives/queue-sqs.test.js +300 -0
  1131. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-cluster.test.js +338 -0
  1132. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-registry.test.js +75 -0
  1133. package/lib/vendor/blamejs/test/layer-0-primitives/redact-dlp.test.js +246 -0
  1134. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +130 -0
  1135. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +335 -0
  1136. package/lib/vendor/blamejs/test/layer-0-primitives/request-log.test.js +170 -0
  1137. package/lib/vendor/blamejs/test/layer-0-primitives/require-auth-cache-control.test.js +93 -0
  1138. package/lib/vendor/blamejs/test/layer-0-primitives/require-mtls.test.js +34 -0
  1139. package/lib/vendor/blamejs/test/layer-0-primitives/resource-access-lock.test.js +52 -0
  1140. package/lib/vendor/blamejs/test/layer-0-primitives/retention-floor.test.js +67 -0
  1141. package/lib/vendor/blamejs/test/layer-0-primitives/retry.test.js +535 -0
  1142. package/lib/vendor/blamejs/test/layer-0-primitives/router-cross-origin-redirect.test.js +0 -0
  1143. package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +128 -0
  1144. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-loops.test.js +163 -0
  1145. package/lib/vendor/blamejs/test/layer-0-primitives/safe-async-parallel.test.js +170 -0
  1146. package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +248 -0
  1147. package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +451 -0
  1148. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +289 -0
  1149. package/lib/vendor/blamejs/test/layer-0-primitives/safe-icap.test.js +206 -0
  1150. package/lib/vendor/blamejs/test/layer-0-primitives/safe-jsonpath.test.js +104 -0
  1151. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +339 -0
  1152. package/lib/vendor/blamejs/test/layer-0-primitives/safe-mount-info.test.js +180 -0
  1153. package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +78 -0
  1154. package/lib/vendor/blamejs/test/layer-0-primitives/safe-sieve.test.js +123 -0
  1155. package/lib/vendor/blamejs/test/layer-0-primitives/safe-smtp.test.js +95 -0
  1156. package/lib/vendor/blamejs/test/layer-0-primitives/safe-url-idn-homograph.test.js +77 -0
  1157. package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +257 -0
  1158. package/lib/vendor/blamejs/test/layer-0-primitives/saml-slo.test.js +249 -0
  1159. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +228 -0
  1160. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-exactly-once.test.js +238 -0
  1161. package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +92 -0
  1162. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +700 -0
  1163. package/lib/vendor/blamejs/test/layer-0-primitives/sd-notify.test.js +67 -0
  1164. package/lib/vendor/blamejs/test/layer-0-primitives/sec-cyber.test.js +85 -0
  1165. package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +107 -0
  1166. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +175 -0
  1167. package/lib/vendor/blamejs/test/layer-0-primitives/seeders.test.js +816 -0
  1168. package/lib/vendor/blamejs/test/layer-0-primitives/self-update-standalone-verifier.test.js +168 -0
  1169. package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +302 -0
  1170. package/lib/vendor/blamejs/test/layer-0-primitives/server-timing.test.js +93 -0
  1171. package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +247 -0
  1172. package/lib/vendor/blamejs/test/layer-0-primitives/session-extensions.test.js +295 -0
  1173. package/lib/vendor/blamejs/test/layer-0-primitives/shape-match.test.js +142 -0
  1174. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +952 -0
  1175. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-multipart-sse.test.js +441 -0
  1176. package/lib/vendor/blamejs/test/layer-0-primitives/slug.test.js +330 -0
  1177. package/lib/vendor/blamejs/test/layer-0-primitives/smtp-policy.test.js +233 -0
  1178. package/lib/vendor/blamejs/test/layer-0-primitives/source-comment-blocks.test.js +105 -0
  1179. package/lib/vendor/blamejs/test/layer-0-primitives/speculation-rules.test.js +319 -0
  1180. package/lib/vendor/blamejs/test/layer-0-primitives/sse.test.js +148 -0
  1181. package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +283 -0
  1182. package/lib/vendor/blamejs/test/layer-0-primitives/standard-webhooks.test.js +67 -0
  1183. package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +266 -0
  1184. package/lib/vendor/blamejs/test/layer-0-primitives/step-up.test.js +487 -0
  1185. package/lib/vendor/blamejs/test/layer-0-primitives/storage-chunk-scratch.test.js +0 -0
  1186. package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +773 -0
  1187. package/lib/vendor/blamejs/test/layer-0-primitives/stream-throttle.test.js +173 -0
  1188. package/lib/vendor/blamejs/test/layer-0-primitives/structured-fields.test.js +180 -0
  1189. package/lib/vendor/blamejs/test/layer-0-primitives/tcpa-10dlc.test.js +66 -0
  1190. package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +89 -0
  1191. package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +571 -0
  1192. package/lib/vendor/blamejs/test/layer-0-primitives/test-harness.test.js +190 -0
  1193. package/lib/vendor/blamejs/test/layer-0-primitives/testing-request.test.js +119 -0
  1194. package/lib/vendor/blamejs/test/layer-0-primitives/testing.test.js +522 -0
  1195. package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +151 -0
  1196. package/lib/vendor/blamejs/test/layer-0-primitives/tls-exporter.test.js +168 -0
  1197. package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-ct.test.js +275 -0
  1198. package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-verify.test.js +105 -0
  1199. package/lib/vendor/blamejs/test/layer-0-primitives/tls-pinset-drift.test.js +35 -0
  1200. package/lib/vendor/blamejs/test/layer-0-primitives/tls-preferred-groups.test.js +81 -0
  1201. package/lib/vendor/blamejs/test/layer-0-primitives/tracing.test.js +280 -0
  1202. package/lib/vendor/blamejs/test/layer-0-primitives/uuid.test.js +93 -0
  1203. package/lib/vendor/blamejs/test/layer-0-primitives/vault-aad.test.js +277 -0
  1204. package/lib/vendor/blamejs/test/layer-0-primitives/vault-seal-pem-file.test.js +252 -0
  1205. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-data.test.js +149 -0
  1206. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-manifest.test.js +92 -0
  1207. package/lib/vendor/blamejs/test/layer-0-primitives/vex.test.js +661 -0
  1208. package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +308 -0
  1209. package/lib/vendor/blamejs/test/layer-0-primitives/web-push-vapid.test.js +144 -0
  1210. package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +674 -0
  1211. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-channels.test.js +360 -0
  1212. package/lib/vendor/blamejs/test/layer-0-primitives/worker-pool.test.js +302 -0
  1213. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +349 -0
  1214. package/lib/vendor/blamejs/test/layer-1-state/api-key.test.js +717 -0
  1215. package/lib/vendor/blamejs/test/layer-5-integration/bundler-output.test.js +444 -0
  1216. package/lib/vendor/blamejs/test/layer-5-integration/guard-host-integration.test.js +597 -0
  1217. package/lib/vendor/blamejs/test/layer-5-integration/security-chaos.test.js +308 -0
  1218. package/lib/vendor/blamejs/test/smoke.js +431 -0
  1219. package/lib/webhooks.js +305 -0
  1220. package/package.json +43 -0
@@ -0,0 +1,3318 @@
1
+ {
2
+ "$schema": "../scripts/release-notes-consolidated-schema.json",
3
+ "minor": "0.8",
4
+ "releases": [
5
+ {
6
+ "version": "0.8.90",
7
+ "date": "2026-05-11",
8
+ "headline": "RFC 8689 REQUIRETLS support via `b.mail.requireTls`",
9
+ "summary": "Per-message TLS-requirement signaling between sender and receiver MTAs. Complements MTA-STS and DANE (policy-side, domain-scoped) with a per-message knob that overrides policy when the operator wants stricter-than-policy delivery — the message bounces instead of falling back to cleartext if no downstream MTA can deliver under TLS.",
10
+ "sections": [
11
+ {
12
+ "heading": "Added",
13
+ "items": [
14
+ {
15
+ "title": "`b.mail.requireTls.peerSupports(ehloLines)`",
16
+ "body": "Walks a parsed EHLO response and returns `true` when the peer advertised the `REQUIRETLS` keyword. Case-insensitive per RFC 5321 §2.4; refuses substring matches (`FOO-REQUIRETLS-BAR` does NOT match); empty / non-array input returns `false`."
17
+ },
18
+ {
19
+ "title": "`b.mail.requireTls.mailFromExtension({ requireTls })`",
20
+ "body": "Builds the trailing `\" REQUIRETLS\"` token to append to a MAIL FROM line. Refuses a non-boolean flag value (a truthy-but-wrong-shape value like `\"yes\"` throws instead of silently succeeding)."
21
+ },
22
+ {
23
+ "title": "`b.mail.requireTls.parseTlsRequiredHeader(headerValue)`",
24
+ "body": "Parses the RFC 8689 §5 `TLS-Required` header. Returns `\"no\"` only when the value is the literal token `no` (case-insensitive, ignoring whitespace) per spec; any other non-empty value returns `\"yes\"` (RFC 8689 §5: \"any value other than 'No' MUST be treated as if the field had been absent\" — conservative strict path); returns `null` for absent / empty / non-string input. Refuses control characters on the raw header value before `trim()` runs so a leading `\\n` / trailing `\\r` / NUL / DEL byte can no longer slip past as the literal token `no` (ASCII HT remains permitted as structural folding whitespace)."
25
+ }
26
+ ]
27
+ }
28
+ ],
29
+ "references": [
30
+ {
31
+ "label": "RFC 8689 SMTP Require TLS Option",
32
+ "url": "https://www.rfc-editor.org/rfc/rfc8689.html"
33
+ },
34
+ {
35
+ "label": "RFC 5321 Simple Mail Transfer Protocol",
36
+ "url": "https://www.rfc-editor.org/rfc/rfc5321.html"
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "version": "0.8.89",
42
+ "date": "2026-05-11",
43
+ "headline": "Hotfix: `b.earlyHints.send()` case-variant link bypass + new `b.mail.srs` Sender Rewriting Scheme",
44
+ "summary": "Closes a case-variant header bypass that let unvalidated `Link` headers reach `writeEarlyHints()` and ships an SRS0 forwarder primitive so the next-hop SPF check passes and bounces route back correctly.",
45
+ "sections": [
46
+ {
47
+ "heading": "Added",
48
+ "items": [
49
+ {
50
+ "title": "`b.mail.srs.create({ secret, forwarderDomain, expiryDays? })`",
51
+ "body": "Sender Rewriting Scheme (SRS0) implementation for forwarder envelope-from rewriting. Returns `{ rewrite, reverse }`. `rewrite(addr)` produces an SRS-encoded `SRS0=HHHH=TT=domain=local@forwarder.example` form; `reverse(srs)` decodes back to the original sender, verifying an HMAC-SHA-256 short-tag (operator-supplied secret), the day-stamp expiry window (default 30 days), and the canonical 4-field SRS0 grammar. Domain-binding: `reverse(srs)` refuses with `srs/wrong-forwarder` when the SRS0 address's `@domain` part doesn't match the rewriter's `forwarderDomain` (case-insensitive per RFC 5321 §2.3.5). Refuses tampered tags via `srs/bad-tag`, expired rewrites via `srs/expired`, double-SRS-encoding via `srs/already-rewritten`, and bad address shapes via `srs/bad-address`. HMAC uses `b.crypto.timingSafeEqual` for constant-time tag comparison."
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "heading": "Fixed",
57
+ "items": [
58
+ {
59
+ "title": "`b.earlyHints.send()` case-variant `Link` header bypass",
60
+ "body": "Pre-v0.8.89, supplying both `link` (lowercase) AND `Link` (capital, or any other case variant) to `b.earlyHints.send()` bypassed the validator. `opts.link` got the dedicated `_validateLink` pass and was assigned to `headers.link`; the trailing header loop then iterated `Object.keys(opts)`, skipped only the exact-match `\"link\"` key, and for `\"Link\"` lowercased the name and wrote `headers.link = opts.Link` — overwriting the validated value with unvalidated content. Malformed Link headers (missing `rel=`, unknown relation, oversized) reached `writeEarlyHints()` despite the API contract. The fix collapses all opt keys to a single canonical lowercase map up front; duplicate case-variants of any header (not just `link`) now refuse with `early-hints/duplicate-header` so operators see the collision instead of silent winner-take-all behavior. Capital `Link` alone (no lowercase variant) still works — it goes through the same validator. Tests added: case-variant-collision refuse, capital-Link-alone validates, capital-Link with malformed value still throws `bad-link`."
61
+ }
62
+ ]
63
+ }
64
+ ],
65
+ "references": [
66
+ {
67
+ "label": "RFC 5321 §2.3.5 (domain name case-insensitivity)",
68
+ "url": "https://www.rfc-editor.org/rfc/rfc5321.html#section-2.3.5"
69
+ },
70
+ {
71
+ "label": "RFC 8297 Early Hints",
72
+ "url": "https://www.rfc-editor.org/rfc/rfc8297.html"
73
+ },
74
+ {
75
+ "label": "SRS specification (Meng Wong, 2003)",
76
+ "url": "https://www.libsrs2.org/srs/srs.pdf"
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "version": "0.8.88",
82
+ "date": "2026-05-11",
83
+ "headline": "Hotfix: `b.auth.fal.meets()` invalid-band authorization-correctness bug + new `b.earlyHints` RFC 8297 helper",
84
+ "summary": "`b.auth.fal.meets(actualBand, requiredBand)` previously compared raw ranks without validating either input. Unknown bands mapped to rank `0`, so `meets(\"FAL1\", \"FALX\")` returned `true` and `meets(\"bad\", \"bad\")` returned `true` — both contradicting the documented contract that invalid bands MUST return `false`. Operators calling `meets()` directly for authorization decisions could grant access on malformed input pairs. Plus a new RFC 8297 103 Early Hints helper.",
85
+ "sections": [
86
+ {
87
+ "heading": "Fixed",
88
+ "items": [
89
+ {
90
+ "title": "`b.auth.fal.meets()` validates both bands",
91
+ "body": "The new implementation validates both bands via `isValidBand()` first; any invalid band on either side returns `false`. The `requireFal()` guard was already correct (it used `meets()` after a separate `isValidBand(actualBand)` check, but a defense-in-depth pass into `meets()` itself now catches direct callers too). Tests added: 7 invalid-input shapes (`FALX` actual, `FALX` required, `bad`/`bad`, `FALX`/`FALX`, null on either side, both null)."
92
+ }
93
+ ]
94
+ },
95
+ {
96
+ "heading": "Added",
97
+ "items": [
98
+ {
99
+ "title": "`b.earlyHints.send(res, { link })` — RFC 8297 103 Early Hints",
100
+ "body": "Wraps Node 18.11+'s built-in `res.writeEarlyHints()` with: link-header validation (RFC 8288 form with one of `preload` / `preconnect` / `prefetch` / `dns-prefetch` / `modulepreload` / `prerender` / `next` / `prev`); silent no-op when the response object lacks `writeEarlyHints` (HTTP/1.0, mocks, older Node); refusal of per-request-state headers per RFC 8297 §3 (`set-cookie`, `authorization`, `content-length`, `content-type`, etc.). Operators use it to start browser-side preload of CSS / JS / fonts / preconnect origins in parallel with the server-side composition of the final response."
101
+ }
102
+ ]
103
+ }
104
+ ],
105
+ "references": [
106
+ {
107
+ "label": "RFC 8297 103 Early Hints",
108
+ "url": "https://www.rfc-editor.org/rfc/rfc8297.html"
109
+ },
110
+ {
111
+ "label": "RFC 8288 Web Linking",
112
+ "url": "https://www.rfc-editor.org/rfc/rfc8288.html"
113
+ }
114
+ ]
115
+ },
116
+ {
117
+ "version": "0.8.87",
118
+ "date": "2026-05-11",
119
+ "headline": "`b.auth.fal` 800-63-4 FAL classifier + RFC 7505 Null-MX helper + Gmail Feedback-ID builder + vendor-update.sh cleanup",
120
+ "summary": "Adds the federation-side counterpart to the existing AAL band classifier (800-63C-4 FAL1/FAL2/FAL3), an RFC 7505 Null-MX classifier for send-side opt-out detection, a Gmail FBL Feedback-ID builder, and a `vendor-update.sh` cleanup that removes a stale argon2 entry.",
121
+ "sections": [
122
+ {
123
+ "heading": "Added",
124
+ "items": [
125
+ {
126
+ "title": "`b.auth.fal` NIST 800-63C-4 FAL classifier",
127
+ "body": "Federation-side counterpart to the existing `b.auth.aal` band classifier. `fromAssertion({ channel, encrypted?, replayProtected?, hokBinding? })` classifies an incoming federation assertion as `\"FAL1\"` / `\"FAL2\"` / `\"FAL3\"` per NIST 800-63C-4: Holder-of-Key (mTLS / DPoP / SAML HoK) with replay-protection → FAL3; back-channel OR encrypted front-channel with replay-protection → FAL2; bare bearer front-channel → FAL1. Conservative: missing replay-protection on a back-channel assertion downgrades to FAL1 because §5.2 requires nonce / jti binding before back-channel can claim FAL2. `requireFal(minimumBand)` builds a band-check guard that throws `auth/fal-insufficient` for stale-band requests."
128
+ },
129
+ {
130
+ "title": "`b.network.dns.isNullMx(records)` — RFC 7505",
131
+ "body": "Returns `true` when an operator-supplied MX-record array signals \"this domain does not accept email\" (single record, priority 0, exchange `.` per RFC 7505 §3). Operators send-side check this before delivery to skip domains that have explicitly opted out — `node:dns.resolveMx` returns `exchange: \"\"` for the same RDATA, so the classifier accepts both shapes."
132
+ },
133
+ {
134
+ "title": "`b.mail.feedbackId({ campaignId, customerId, mailType, senderId })`",
135
+ "body": "Builds a Gmail Feedback-Loop (FBL) Feedback-ID header value as the canonical 4-tuple `CampaignID:CustomerID:MailType:SenderID`. Refuses missing / empty fields, fields containing `:` (would corrupt the field separator), fields >64 chars (Gmail FBL truncation threshold), and control-char content (CR/LF header-injection defense). Setting Feedback-ID on outbound mail lets Gmail Postmaster Tools surface per-campaign abuse-rate metrics keyed by the operator's vocabulary instead of by SMTP envelope-sender alone."
136
+ }
137
+ ]
138
+ },
139
+ {
140
+ "heading": "Fixed",
141
+ "items": [
142
+ {
143
+ "title": "`vendor-update.sh --check` removed stale argon2 entry",
144
+ "body": "argon2 was removed from `lib/vendor/` when Node 24's built-in `crypto.argon2*` API replaced the third-party prebuilds (per `lib/argon2-builtin.js`); the script still listed it in the check array, producing a false \"UPDATE AVAILABLE\" line for an unvendored package. The case-block error path that still says \"argon2 is no longer vendored\" stays so anyone running `./scripts/vendor-update.sh argon2` gets the operator-friendly explanation."
145
+ }
146
+ ]
147
+ }
148
+ ],
149
+ "references": [
150
+ {
151
+ "label": "NIST SP 800-63C-4",
152
+ "url": "https://pages.nist.gov/800-63-4/sp800-63c.html"
153
+ },
154
+ {
155
+ "label": "RFC 7505 Null-MX",
156
+ "url": "https://www.rfc-editor.org/rfc/rfc7505.html"
157
+ }
158
+ ]
159
+ },
160
+ {
161
+ "version": "0.8.86",
162
+ "date": "2026-05-11",
163
+ "headline": "Sectoral + cybersecurity posture sweep + HTTP-hygiene primitives + npm-publish hotfix",
164
+ "summary": "v0.8.85's `npm audit signatures` step failed with `npm error found no installed dependencies to audit` because the framework's zero-runtime-deps posture produces an empty install tree; the gate now treats that specific message as success. v0.8.85 npm tarball never published — operators upgrade `0.8.83 → 0.8.86` to pick up the carried v0.8.84 + v0.8.85 surface plus the new v0.8.86 primitives.",
165
+ "sections": [
166
+ {
167
+ "heading": "Added",
168
+ "items": [
169
+ {
170
+ "title": "New sectoral + cybersecurity compliance postures",
171
+ "body": "`cmmc-2.0` (DoD Cybersecurity Maturity Model Certification 2.0), `cjis-v6` (FBI CJIS Security Policy v6.0), `iso-27001-2022` + `iso-27002-2022` + `iso-27017` + `iso-27018` + `iso-27701` (ISO/IEC 27001 family), `nist-800-66-r2` (HIPAA Security Rule implementation guidance), `ehds` (European Health Data Space), `circia` (US Cyber Incident Reporting for Critical Infrastructure Act). Cascade defaults set encrypted-backup + signed-audit-chain + TLS 1.3 + vacuum-after-erase for the data-tier postures; `iso-27002-2022` + `circia` defer the data-tier mandate to operator choice."
172
+ },
173
+ {
174
+ "title": "`b.cacheStatus` — RFC 9211 Cache-Status",
175
+ "body": "Response-header builder + parser. `append(prev, entry)` chains the operator's current cache decision onto whatever upstream caches wrote; `entry({...})` formats a single entry; `parse(headerValue)` returns the parsed chain as `[{ cache, params }]` records with `hit` / `stored` / `collapsed` as booleans, `ttl` / `fwdStatus` as numbers, `fwd` as the RFC 9211 §2 enum string, `key` / `detail` as unquoted sf-strings."
176
+ },
177
+ {
178
+ "title": "`b.serverTiming` — W3C Server-Timing",
179
+ "body": "`create()` returns a per-request collector with `mark(name, durationMs?, description?)` / `measure(name, fn)` async-timing wrapper / `toHeader()` serializer. Surfaces server-side latency in the browser's Performance API."
180
+ },
181
+ {
182
+ "title": "`b.middleware.noCache` — RFC 9111 §5.2.2.5",
183
+ "body": "`Cache-Control: no-store` middleware for auth-gated / individualized response paths. Sets `Cache-Control: no-store`, `Pragma: no-cache` (HTTP/1.0 compatibility), `Vary: Cookie, Authorization` so intermediate caches don't store personalized responses keyed by URL alone. Optional `opts.when(req)` predicate for conditional application; `opts.skipExisting: true` skips when `Cache-Control` is already set."
184
+ }
185
+ ]
186
+ },
187
+ {
188
+ "heading": "Fixed",
189
+ "items": [
190
+ {
191
+ "title": "npm-publish gate treats empty install tree as success",
192
+ "body": "`npm audit signatures` step in `npm-publish.yml` now treats the `npm error found no installed dependencies to audit` message as success while keeping every other failure mode loud."
193
+ }
194
+ ]
195
+ }
196
+ ],
197
+ "references": [
198
+ {
199
+ "label": "RFC 9211 Cache-Status",
200
+ "url": "https://www.rfc-editor.org/rfc/rfc9211.html"
201
+ },
202
+ {
203
+ "label": "RFC 9111 HTTP Caching",
204
+ "url": "https://www.rfc-editor.org/rfc/rfc9111.html"
205
+ }
206
+ ]
207
+ },
208
+ {
209
+ "version": "0.8.85",
210
+ "date": "2026-05-11",
211
+ "headline": "MCP tool registry + tool-call signing + A2A v1 task-exchange surface",
212
+ "summary": "Closes substantial agent-protocol gaps. MCP tool registry signs every tool descriptor and adds tool-call envelope signing (defends compromised MCP server / descriptor drift, MCP-middleman / indirect-prompt-injection synthesized calls, and call-replay). A2A v1 task-exchange surface ships client dispatchers + server middleware for `tasks/send`, `tasks/get`, `tasks/cancel` plus a signed Agent Card at `/.well-known/agent.json`.",
213
+ "sections": [
214
+ {
215
+ "heading": "Added",
216
+ "items": [
217
+ {
218
+ "title": "`b.mcp.toolRegistry.create({ tools, signingKey, verifyingKey?, alg?, ttlMs? })`",
219
+ "body": "Every registered tool gets a signed descriptor blob `{ tool, alg, signature }` (defense against compromised MCP server / descriptor drift) and a `descriptorsManifest()` produces a signed `{ body, signature }` document for operator-side attestation. The registry's `signCall({ toolName, args, nonce?, ttlMs? })` builds + signs an outbound tool-call envelope `{ tool, argsHash, nonce, iat, exp }`; `verifyCall(signed, { args?, seen?, nowMs? })` runs the inverse on inbound — refuses signature mismatch (`mcp/call-verify-failed`), expired envelopes (`mcp/call-expired`), replayed nonces via operator-supplied `seen(nonce)` callback (`mcp/call-replay`), unregistered tools (`mcp/call-unregistered-tool`), and args-hash mismatch when raw args supplied (`mcp/call-args-mismatch`). Default algorithm ML-DSA-87 per the framework's PQC-first rule; Ed25519 / ECDSA / SLH-DSA also available."
220
+ },
221
+ {
222
+ "title": "A2A v1 task-exchange surface",
223
+ "body": "`b.a2a.tasks.{ send, get, cancel }` (client-side JSON-RPC dispatchers — `send` posts `tasks/send` to the peer URL with task validation + https-only refusal; `get` polls `tasks/get`; `cancel` requests `tasks/cancel`). `b.a2a.middleware.tasks({ scopes, handler, maxBytes? })` (server-side connect-style middleware — parses inbound JSON-RPC 2.0, enforces method allowlist `[tasks/send, tasks/get, tasks/cancel]` with -32601 method-not-found, enforces per-skill scopes via `req.a2aScopes` with -32001 scope-denied, dispatches to operator handler, maps errors to JSON-RPC -32603, refuses non-POST with 405 + non-JSON content-type with 415). `b.a2a.middleware.agentCard({ card, maxAgeSec? })` serves operator's signed Agent Card at `/.well-known/agent.json` per A2A v1 discovery — 405 on non-GET, Cache-Control max-age operator-tunable."
224
+ }
225
+ ]
226
+ }
227
+ ]
228
+ },
229
+ {
230
+ "version": "0.8.84",
231
+ "date": "2026-05-11",
232
+ "headline": "Supply-chain hardening trio + HTTP-API hygiene primitives (RFC 9457 + idempotency-key)",
233
+ "summary": "Adds CodeQL SAST workflow on `security-extended`, `npm audit signatures` step in `npm-publish.yml`, vendored-SBOM cosign signing, and ships `b.problemDetails` (RFC 9457) + `b.middleware.idempotencyKey` (draft-ietf-httpapi-idempotency-key). The v0.8.84 git tag landed on a wrong commit due to a release-workflow ordering issue and the npm publish did not ship; operators upgrade directly from 0.8.83 to 0.8.85.",
234
+ "sections": [
235
+ {
236
+ "heading": "Added",
237
+ "items": [
238
+ {
239
+ "title": "CodeQL SAST workflow",
240
+ "body": "`.github/workflows/codeql.yml` runs on PR + push-to-main + weekly Mon-05:31-UTC schedule against the JavaScript surface using the `security-extended` query pack (catches SQL injection, XSS, prototype pollution, command injection, ReDoS, unsafe deserialization, SSRF, hardcoded credentials beyond what OSV-Scanner sees; findings surface as SARIF in the Security tab)."
241
+ },
242
+ {
243
+ "title": "`npm audit signatures` step in `npm-publish.yml`",
244
+ "body": "Verifies the cryptographic signing chain for every package in the install tree against the npm registry's public keys before the publish step runs. Regression-defense — the framework ships zero npm runtime deps so the tree is trivially empty today; if a future patch accidentally adds a runtime dep, the gate refuses an unsigned registry entry before tag-push triggers a release."
245
+ },
246
+ {
247
+ "title": "Vendored-SBOM cosign signing",
248
+ "body": "Extends the existing cosign-keyless flow to sign `sbom.vendored.cdx.json` alongside `sbom.cdx.json` and attaches both `.sigstore` bundles to the GitHub Release."
249
+ },
250
+ {
251
+ "title": "`b.problemDetails` (RFC 9457)",
252
+ "body": "`create({ type, title, status, detail, instance, ...extensions })` builds a frozen problem doc with field validation; `fromError(err)` converts a `FrameworkError` into a problem doc with `type` derived from `err.code` against a configurable base URI; `respond(res, problem)` writes the response with `Content-Type: application/problem+json` + `Cache-Control: no-store` per RFC 9457 §3 + RFC 9111 §5.2.2.5; `validate(doc)` parses inbound problem docs from upstream APIs with shape refusal."
253
+ },
254
+ {
255
+ "title": "`b.middleware.idempotencyKey` (draft-ietf-httpapi-idempotency-key)",
256
+ "body": "Replay-safe POST / PUT / PATCH / DELETE — operator-supplied store interface with first-party `memoryStore({ maxEntries })`. Cached fingerprint = `method + path + sha3-256(body)`. 422 + `idempotency/key-reuse-mismatch` problem-details on same-key-different-body per draft §4.3. 5xx responses are NOT cached (replaying a transient infrastructure failure is not idempotent). Default TTL 24h; default methods POST / PUT / PATCH / DELETE."
257
+ }
258
+ ]
259
+ }
260
+ ],
261
+ "references": [
262
+ {
263
+ "label": "RFC 9457 Problem Details for HTTP APIs",
264
+ "url": "https://www.rfc-editor.org/rfc/rfc9457.html"
265
+ },
266
+ {
267
+ "label": "draft-ietf-httpapi-idempotency-key",
268
+ "url": "https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key/"
269
+ }
270
+ ]
271
+ },
272
+ {
273
+ "version": "0.8.83",
274
+ "date": "2026-05-11",
275
+ "headline": "ACME 47-day-cert readiness — certificate profiles + dns-account-01 challenge + ARI renewal-window jitter",
276
+ "summary": "Adds draft-aaron-acme-profiles, draft-ietf-acme-dns-account-label, and RFC 9773 §4.2 fleet-scheduling jitter on `b.acme` so operators distribute renewal storms uniformly across the CA-suggested window.",
277
+ "sections": [
278
+ {
279
+ "heading": "Added",
280
+ "items": [
281
+ {
282
+ "title": "`acme.listProfiles()` + `acme.newOrder({ profile })`",
283
+ "body": "draft-aaron-acme-profiles. `listProfiles()` reads `directory.meta.profiles` and returns the CA-advertised `{ name: description }` map. `newOrder({ profile })` passes the chosen profile name through the order payload; refuses non-string + caps length at 64 bytes. As CA/B Forum SC-081v3 phases in the 47-day mandate, profile-name vocabulary becomes the operator-facing handle for \"long-lived\" vs \"47-day\" vs \"short-lived\" cert selection."
284
+ },
285
+ {
286
+ "title": "`acme.dnsAccount01ChallengeRecord(token, { identifier, ttl? })`",
287
+ "body": "draft-ietf-acme-dns-account-label. Builds the per-account-scoped TXT record (`_<accountLabel>._acme-challenge.<host>`) where `accountLabel` is the lowercase base32 of the first 80 bits of `SHA-256(accountUrl)`. Refuses pre-newAccount (label needs accountUrl as seed); caps identifier at 255 bytes; refuses negative / huge TTL."
288
+ },
289
+ {
290
+ "title": "`acme.renewIfDue({ jitter: true })` fleet-scheduling jitter",
291
+ "body": "RFC 9773 §4.2. Returns a `renewAt` ISO timestamp picked uniformly across the CA-suggested window so operator fleets running on the same poll cadence stop clustering their renewal storms at the window-start instant. Default behavior (`jitter` off or absent) preserves pre-0.8.83 \"renew now\" semantics. The `acme.cert.renew.scheduled` audit row carries the chosen `renewAt` when jitter is on."
292
+ }
293
+ ]
294
+ }
295
+ ],
296
+ "references": [
297
+ {
298
+ "label": "RFC 9773 ACME Renewal Information",
299
+ "url": "https://www.rfc-editor.org/rfc/rfc9773.html"
300
+ },
301
+ {
302
+ "label": "draft-aaron-acme-profiles",
303
+ "url": "https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/"
304
+ },
305
+ {
306
+ "label": "draft-ietf-acme-dns-account-label",
307
+ "url": "https://datatracker.ietf.org/doc/draft-ietf-acme-dns-account-label/"
308
+ }
309
+ ]
310
+ },
311
+ {
312
+ "version": "0.8.82",
313
+ "date": "2026-05-11",
314
+ "headline": "Privacy 2026 posture sweep — 27 new compliance postures",
315
+ "summary": "Closes the privacy gap with US-federal, UK, Latin America, APAC, US-state child-privacy, and EU non-personal-data postures. Introduces new REGIME_MAP `domain` values (`child-privacy`, `financial-privacy`, `consumer-privacy`, `genetic-privacy`, `platform-governance`, `identity`) so dashboards grouped by domain pick up the new buckets via `b.compliance.posturesByDomain(domain)` without code changes.",
316
+ "sections": [
317
+ {
318
+ "heading": "Added",
319
+ "items": [
320
+ {
321
+ "title": "US federal postures",
322
+ "body": "`coppa` + `coppa-2025` (FTC final rule 2025-04-22, effective 2026-06-23 — biometric expansion + knowing-collection-13-and-under disclosure; cascade adds `backupEncryptionRequired: true` + vacuum-after-erase), `glba-safeguards` (Safeguards Rule 2024 Amendment, effective 2024-05-13), `gina` (Genetic Information Nondiscrimination Act), `vppa` (Video Privacy Protection Act), `can-spam`, `il-gipa` (Illinois Genetic Information Privacy Act with post-2024 private right of action), `hhs-repro-24` (HHS Reproductive Health HIPAA Amendment 2024-12-23), `nist-pf-1.1` (NIST Privacy Framework 1.1, final 2025-04-14)."
323
+ },
324
+ {
325
+ "title": "UK + Latin America postures",
326
+ "body": "`uk-duaa` (Data (Use and Access) Act 2025 — Royal Assent 2025-06-19; replaces the abandoned DPDI Bill). `cl-pdpa` (Chile Ley 21.719, enacted 2024-12-13, effective 2026-12-01), `mx-lfpdppp` (Mexico 2025 secondary reform), `ar-pdpa` (Argentina Ley 25.326)."
327
+ },
328
+ {
329
+ "title": "APAC postures",
330
+ "body": "`pipa-kr` (Korea PIPA 2023 major amendment, phased 2023-09-15 / 2024-03-15), `au-privacy` (Australia Privacy Act + 2024 Amendment Act — statutory tort effective 2025-06-10), `th-pdpa`, `vn-pdp` (Vietnam PDP Law effective 2026-01-01), `id-pdp` (Indonesia PDP Law effective 2024-10-17), `my-pdpa` (Malaysia 2024 amendments effective 2025-04-30)."
331
+ },
332
+ {
333
+ "title": "US-state child-privacy postures",
334
+ "body": "`ny-safe-kids` + `ny-saffe` (NY Child Data Protection Act + Stop Addictive Feeds Exploitation, both effective 2025-06-20), `md-kids-code` (Maryland Age-Appropriate Design Code), `vt-aadc` (Vermont AADC)."
335
+ },
336
+ {
337
+ "title": "EU non-personal-data + adjacent postures",
338
+ "body": "`dsa` (Digital Services Act, fully applicable 2024-02-17), `dga` (Data Governance Act, applicable 2023-09-24), `eu-cer` (Critical Entities Resilience Directive 2022/2557, transposition 2024-10-17), `eu-cyber-sol` (Cyber Solidarity Act 2025/38, effective 2025-02-04), `eidas-2` (eIDAS 2 / EUDI Wallet, rollout 2026-2027)."
339
+ },
340
+ {
341
+ "title": "New REGIME_MAP `domain` values",
342
+ "body": "`child-privacy`, `financial-privacy`, `consumer-privacy`, `genetic-privacy`, `platform-governance`, `identity`. Operators rendering compliance dashboards grouped by domain pick up the new buckets via `b.compliance.posturesByDomain(domain)` without code changes."
343
+ }
344
+ ]
345
+ }
346
+ ]
347
+ },
348
+ {
349
+ "version": "0.8.81",
350
+ "date": "2026-05-11",
351
+ "headline": "AI-governance postures + ISO 42001 / 23894 cross-walk + privacy catalog drift fixes",
352
+ "summary": "18 new postures register in `b.compliance.KNOWN_POSTURES` (state AI governance, international AI, AI management standards, California gen-AI content credentials, substrate-to-posture cleanup, plus `fl-fdbr` and the long-missing `dpdp`). Adds an ISO 42001 + 23894 cross-walk for operators chasing ISO certification under AI Act high-risk scope, and corrects state-privacy citation drift.",
353
+ "sections": [
354
+ {
355
+ "heading": "Added",
356
+ "items": [
357
+ {
358
+ "title": "AI-governance posture additions",
359
+ "body": "State AI governance: `co-ai`, `il-hb3773`, `tx-traiga`, `ut-aipa`, `nyc-ll144`, `ca-tfaia` (frontier AI critical-incident records cascade to `backupEncryptionRequired: true`). International AI: `kr-ai-basic`, `cn-ai-label`. AI management standards: `iso-42001`, `iso-23894`. California gen-AI content credentials: `ca-sb942`, `ca-ab853`. Substrate-to-posture cleanup: `eaa` for EU Accessibility Act, `wcag-2-2` for `b.guardHtml.wcag`, `eu-data-act` for `b.dataAct`, `hitech` extending HIPAA-tier, `ferpa` for student records. Plus `fl-fdbr` (Florida Digital Bill of Rights) and the long-missing `dpdp` (India DPDP Act 2023)."
360
+ },
361
+ {
362
+ "title": "ISO 42001 + 23894 cross-walks",
363
+ "body": "`b.compliance.aiAct.crossWalkIso42001([aiActCitation])` and `crossWalkIso23894()` return a 15-row mapping table linking EU AI Act articles (Art. 9 risk management → Art. 73 incident reporting) to ISO/IEC 42001:2023 Annex A controls and ISO/IEC 23894:2023 risk-management clauses. Read-only metadata, defensive copies returned, no behavior change at deploy time."
364
+ }
365
+ ]
366
+ },
367
+ {
368
+ "heading": "Fixed",
369
+ "items": [
370
+ {
371
+ "title": "`b.compliance.set(\"dpdp\")` now resolves",
372
+ "body": "India DPDP Act 2023 was in the `POSTURE_DEFAULTS` cascade table but not in `KNOWN_POSTURES`, so `b.compliance.set(\"dpdp\")` threw `compliance/unknown-posture`."
373
+ },
374
+ {
375
+ "title": "DSR drift fix for `fl-fdbr`",
376
+ "body": "`b.dsr.stateRules(\"fl-fdbr\")` / `stateRules(\"FL\")` now resolve (45-day response window, 15-day extension, 30-day cure, profiling opt-out enabled, minor opt-in 13)."
377
+ },
378
+ {
379
+ "title": "State-privacy citation drift",
380
+ "body": "Four state-privacy posture citations corrected from `(effective 2026-MM-DD)` to `(effective 2025-MM-DD)` — `modpa`, `nh-nhpa`, `nj-njdpa`, `mn-mncdpa` all took effect during 2025; the year-late citations would have surfaced as audit-trail discrepancies under operator review."
381
+ }
382
+ ]
383
+ }
384
+ ]
385
+ },
386
+ {
387
+ "version": "0.8.80",
388
+ "date": "2026-05-10",
389
+ "headline": "Bug fix — `b.config.loadDbBacked` overlapping-tick race",
390
+ "summary": "`cfg.refresh()` calls `_tick()` directly and the periodic poller also invokes `_tick()` independently. When two ticks overlap, the older read could resolve LAST and overwrite a newer config write — so `admin-save → await cfg.refresh()` was not guaranteed to leave the latest value active when `fetchRows` latency varied. Sequence-numbered ticks now drop stale apply attempts.",
391
+ "sections": [
392
+ {
393
+ "heading": "Fixed",
394
+ "items": [
395
+ {
396
+ "title": "Sequence-numbered ticks drop stale apply attempts",
397
+ "body": "Every tick claims a monotonic sequence number at start; at apply-time, ticks whose sequence is older than the last-applied sequence drop with a `config.reload.skipped` audit emission (stale-tick reason). The high-water mark advances ONLY after `cfg.reload` succeeds — a newer tick whose validation fails must not suppress an older in-flight tick that still has valid data (otherwise `refresh(valid)` followed by `refresh(invalid)` could silently keep stale config active). Fetch / transform failures short-circuit before the apply path and likewise do NOT advance the watermark."
398
+ }
399
+ ]
400
+ }
401
+ ]
402
+ },
403
+ {
404
+ "version": "0.8.78",
405
+ "date": "2026-05-10",
406
+ "headline": "Save-triggered reload for `b.config.loadDbBacked`",
407
+ "summary": "Admin save handlers / settings-management UIs that write a row in `_blamejs_config_overrides` now call `await cfg.refresh()` immediately after the write, so the new value is active without waiting for the poll's `intervalMs` tick. The poll stays in place as a safety-net for drift.",
408
+ "sections": [
409
+ {
410
+ "heading": "Added",
411
+ "items": [
412
+ {
413
+ "title": "`cfg.refresh()` save-triggered reload",
414
+ "body": "Returns a `Promise<void>` of identical shape to `cfg.hydrated`: resolves after the tick settles (success OR audit-on-failure), NEVER rejects so save handlers don't deadlock on a flaky DB. The existing `cfg.subscribe(fn)` continues to fire synchronously inside every successful reload — operators reach for it to invalidate caches / recompute derived state / hot-rebuild middleware that closed over the previous config. Three-tier precedence is documented explicitly in the `@primitive` block: DB-row overlay > `opts.env` baseline > schema `default(...)`."
415
+ }
416
+ ]
417
+ }
418
+ ]
419
+ },
420
+ {
421
+ "version": "0.8.77",
422
+ "date": "2026-05-10",
423
+ "headline": "OAuth RS completeness + vendored-deps SBOM + MCP coverage + ACME completeness + SCIM 2.0 + AI Act forward templates + C2PA COSE + US-state postures + config reactive value + idempotent startup hydration",
424
+ "summary": "Closes ten substantial gaps: RFC 7662 / 7591 / 8628 / 8693 OAuth resource-server completeness, vendored-deps SBOM signing, MCP `assertProtocolVersion` + sampling/elicitation guards, ACME `revokeCert` / `accountKeyRollover` / `deactivateAccount` / `tlsAlpn01` / EAB, Permissions-Policy denylist expansion, NIST control crosswalk catalog, SCIM 2.0 server middleware, CRA Annex VIII + AI Act Article 27 FRIA + GPAI Article 53(1)(d) templates, C2PA COSE_Sign1 wrap, 22 new US-state privacy postures + per-state DSR rules. Plus a reactive `cfg.value` + first-immediate-tick hydration + `resetAll()` on the rate-limit module.",
425
+ "sections": [
426
+ {
427
+ "heading": "Added",
428
+ "items": [
429
+ {
430
+ "title": "OAuth resource-server completeness",
431
+ "body": "`b.auth.oauth.introspectToken` (RFC 7662), `registerClient` (RFC 7591 — refuses empty redirect_uris), `deviceAuthorization` + `pollDeviceCode` (RFC 8628 with slow_down / authorization_pending handling), `exchangeToken` (RFC 8693 subject+actor delegation), `b.middleware.protectedResourceMetadata` serving `.well-known/oauth-protected-resource` (draft-ietf-oauth-resource-metadata)."
432
+ },
433
+ {
434
+ "title": "Vendored-deps SBOM",
435
+ "body": "`scripts/build-vendored-sbom.js` emits `sbom.vendored.cdx.json` (CycloneDX 1.6) covering every `lib/vendor/*` bundle with per-file SHA-256 + purl + license metadata; wired into `npm-publish.yml` so OSV-Scanner now scans it alongside the primary `sbom.cdx.json`."
436
+ },
437
+ {
438
+ "title": "MCP endpoint coverage",
439
+ "body": "`b.mcp.assertProtocolVersion` (MCP 2025-11-25 §4.1 header). `b.mcp.sampling.guard({ maxRequestsPerSession, maxMessagesPerRequest, maxTokensPerRequest, allowedModelHints })` (HIGH-RISK endpoint — confused-deputy class). `b.mcp.elicitation.guard` (prompt-injection scan + schema-type allowlist + size cap)."
440
+ },
441
+ {
442
+ "title": "ACME completeness for the 47-day cert lifetime",
443
+ "body": "`revokeCert` (RFC 8555 §7.6), `accountKeyRollover` (§7.3.5), `deactivateAccount` (§7.3.6), `tlsAlpn01KeyAuthorization` (RFC 8737), External Account Binding opt on `newAccount` (§7.3.4 — required by ZeroSSL / Buypass / Google CA). Closes 47-day CA/B forum surface before March 2026 effective date."
444
+ },
445
+ {
446
+ "title": "Permissions-Policy denylist expansion",
447
+ "body": "Adds `identity-credentials-get`, `attribution-reporting-cross-site`, `publickey-credentials-create`, `join-ad-interest-group`, `run-ad-auction`, `shared-storage`, `shared-storage-select-url`, `smartcard`, `all-screens-capture`, `deferred-fetch`."
448
+ },
449
+ {
450
+ "title": "`b.nistCrosswalk` control catalog",
451
+ "body": "Catalog mapping `800-53r5` (~50 controls), `csf-2.0` (~22 functions), `800-171r3` (~25 requirements), `800-218` (SSDF tasks) to framework primitives — used by operators producing SSPs, POAMs, ATO packages, CMMC self-assessments."
452
+ },
453
+ {
454
+ "title": "`b.middleware.scimServer` (SCIM 2.0)",
455
+ "body": "Implements RFC 7642 / 7643 / 7644 — Users + Groups + ServiceProviderConfig + ResourceTypes + Schemas + filter parser (eq / ne / co / sw / ew / pr / gt / ge / lt / le) + GET / POST / PUT / PATCH / DELETE dispatch + bearer-auth callback hook + 1 MiB body cap."
456
+ },
457
+ {
458
+ "title": "CRA + EU AI Act forward-deadline templates",
459
+ "body": "`b.cra.conformityAssessment` Annex VIII technical dossier scaffold (CE marking, Module routing, vuln-handling auto-fill). `b.complianceAiAct.fundamentalRightsImpactAssessment` (Article 27 FRIA template — mandatory for Annex III §5-8 deployers). `b.complianceAiAct.gpai.trainingDataSummary` (Article 53(1)(d) AI Office template — mandatory 2026-08-02)."
460
+ },
461
+ {
462
+ "title": "`b.contentCredentials.signCose` (C2PA COSE_Sign1)",
463
+ "body": "Produces RFC 9052 COSE_Sign1 CBOR envelope with x5chain header + ML-DSA-87 / ed25519 / es256 / 384 / 512 / SLH-DSA-SHAKE-256f algorithms. Interops with c2patool / JPEG Trust / Adobe verifiers (current `sign()` ships a blamejs-internal envelope; the new `signCose()` ships the canonical wire format)."
464
+ },
465
+ {
466
+ "title": "US-state compliance postures + per-state DSR rules",
467
+ "body": "`vcdpa`, `co-cpa`, `ctdpa`, `ucpa`, `tdpsa`, `or-cpa`, `mt-cdpa`, `ia-icdpa`, `in-indpa`, `de-dpdpa`, `nh-nhpa`, `nj-njdpa`, `ky-kcdpa`, `tn-tipa`, `mn-mncdpa`, `ri-ricpa`, `ne-dpa`, `nv-sb370`, `ca-aadc`, `ct-sb3`, `tx-cubi` (plus existing `modpa` + `quebec-25`). Registered in `b.compliance` + per-state DSR rules via `b.dsr.stateRules(state)` / `b.dsr.listStateRules()` returning `{ responseDays, extensionDays, cureDays, profilingOptOut, minorOptIn, notes }`."
468
+ }
469
+ ]
470
+ },
471
+ {
472
+ "heading": "Changed",
473
+ "items": [
474
+ {
475
+ "title": "`b.middleware.rateLimit` instance gains `.resetAll()` + module-level registry",
476
+ "body": "In-memory backends only; cluster backend no-ops per multi-replica race-safety. The module keeps a registry of every rate-limit middleware created in the process. Incident-response scripts can enumerate every limiter and flush state across the whole process without threading references through the app code. `create()` registers; `middleware.close()` deregisters. Top-level `resetAll()` returns the count of instances it walked."
477
+ },
478
+ {
479
+ "title": "`b.config.loadDbBacked` gains `transformValue`",
480
+ "body": "Per-row transform applied between `fetchRows` and schema validation; common shape is unsealing a `b.vault`-sealed ciphertext column so canonical secrets live encrypted-at-rest in `_blamejs_config_overrides`. Per-row failures emit `config.reload.failed` and skip the row so a single bad row can't crash the poller."
481
+ },
482
+ {
483
+ "title": "`b.cryptoField` gains `sealDoc` / `unsealDoc` doc-shaped aliases",
484
+ "body": "Aliases of the existing `sealRow` / `unsealRow` — same identity, lets downstream tests reach for the document-naming convention when preparing seed objects via raw `INSERT`."
485
+ }
486
+ ]
487
+ },
488
+ {
489
+ "heading": "Fixed",
490
+ "items": [
491
+ {
492
+ "title": "`b.config` reactive `value` getter",
493
+ "body": "`cfg.value.X` now reflects the latest validated state after every `reload()` (and every `loadDbBacked` poll). Before, `cfg.value` was a captured property pinned to the create-time object, so `cfg.value.FEATURE_X` stayed stale and only `cfg.get(\"FEATURE_X\")` saw updates. Now backed by an `Object.defineProperty` getter; `cfg.get()` / `cfg.has()` semantics unchanged."
494
+ },
495
+ {
496
+ "title": "`b.config.loadDbBacked` startup hydration window",
497
+ "body": "`loadDbBacked` returned a config handle that stayed at env-only defaults for the first `intervalMs` because `safeAsync.repeating` is `setInterval`-shaped (no t=0 fire). The handle now kicks off one immediate hydration `_tick()` on construction and exposes `cfg.hydrated` — a Promise that resolves after the first tick settles. The Promise NEVER rejects (per-tick failures route through audit, last-good value stays)."
498
+ },
499
+ {
500
+ "title": "`b.middleware._modules.rateLimit.instances()` + module-level `.resetAll()`",
501
+ "body": "Module-level helpers for incident-response scripts to enumerate every limiter and flush state across the whole process. `create()` registers, `middleware.close()` deregisters, top-level `resetAll()` returns the count."
502
+ }
503
+ ]
504
+ }
505
+ ],
506
+ "references": [
507
+ {
508
+ "label": "RFC 7662 OAuth Token Introspection",
509
+ "url": "https://www.rfc-editor.org/rfc/rfc7662.html"
510
+ },
511
+ {
512
+ "label": "RFC 7591 OAuth Dynamic Client Registration",
513
+ "url": "https://www.rfc-editor.org/rfc/rfc7591.html"
514
+ },
515
+ {
516
+ "label": "RFC 8628 OAuth Device Authorization Grant",
517
+ "url": "https://www.rfc-editor.org/rfc/rfc8628.html"
518
+ },
519
+ {
520
+ "label": "RFC 8693 OAuth Token Exchange",
521
+ "url": "https://www.rfc-editor.org/rfc/rfc8693.html"
522
+ },
523
+ {
524
+ "label": "RFC 8555 ACME",
525
+ "url": "https://www.rfc-editor.org/rfc/rfc8555.html"
526
+ },
527
+ {
528
+ "label": "RFC 8737 ACME tls-alpn-01",
529
+ "url": "https://www.rfc-editor.org/rfc/rfc8737.html"
530
+ },
531
+ {
532
+ "label": "SCIM 2.0 RFC 7644",
533
+ "url": "https://www.rfc-editor.org/rfc/rfc7644.html"
534
+ }
535
+ ]
536
+ },
537
+ {
538
+ "version": "0.8.76",
539
+ "date": "2026-05-10",
540
+ "headline": "CI green-up — OSV-Scanner v2 requires CycloneDX SBOM filename match",
541
+ "summary": "OSV-Scanner v2 refuses to parse SBOMs whose filename doesn't match the CycloneDX recognized-pattern spec — `sbom.cyclonedx.json` is NOT recognized; only `bom.json` / `*.cdx.json` / `*.spdx.json` etc. are. v0.8.75's npm-publish workflow failed with `Failed to parse SBOM \"sbom.cyclonedx.json\": Invalid SBOM filename`.",
542
+ "sections": [
543
+ {
544
+ "heading": "Changed",
545
+ "items": [
546
+ {
547
+ "title": "Rename `sbom.cyclonedx.json` → `sbom.cdx.json` everywhere",
548
+ "body": "Workflow generation step, post-process script, OSV scan target, cosign sign target, GH release asset upload, `package.json` `files` array, `scripts/check-pack-against-gitignore.js` allowlist, `.gitignore` allowlist. Published-tarball asset filename changes from `sbom.cyclonedx.json` to `sbom.cdx.json` — consumers reading the SBOM out of the install tree should update the path."
549
+ }
550
+ ]
551
+ }
552
+ ]
553
+ },
554
+ {
555
+ "version": "0.8.75",
556
+ "date": "2026-05-10",
557
+ "headline": "CI green-up — OSV-Scanner v2 removed the `--fail-on-vuln=<severity>` flag",
558
+ "summary": "OSV-Scanner v2.3.5 removed `--fail-on-vuln=<severity>`; passing it now errors with `flag provided but not defined: -fail-on-vuln` and the npm-publish workflow exits 1 before `npm publish` runs. v0.8.73 + v0.8.74's npm-publish workflows both failed for this reason. v2's default behaviour is exit-1-on-ANY-finding — stricter than v1's `--fail-on-vuln=HIGH` floor, and appropriate for a zero-npm-runtime-dep framework where any surfaced vuln means a vendor refresh is overdue. The framework currently has no findings, so the stricter floor is a no-op at HEAD.",
559
+ "sections": [
560
+ {
561
+ "heading": "Fixed",
562
+ "items": [
563
+ {
564
+ "title": "Drop the unsupported `--fail-on-vuln` flag from the OSV-Scanner step",
565
+ "body": "v2's default is exit-1-on-ANY-finding; the previous v1 `--fail-on-vuln=HIGH` floor is no longer settable as a flag, and the stricter v2 default is what a zero-npm-runtime-dep framework wants anyway."
566
+ }
567
+ ]
568
+ }
569
+ ]
570
+ },
571
+ {
572
+ "version": "0.8.74",
573
+ "date": "2026-05-10",
574
+ "headline": "`.gitattributes` `*.sh text eol=lf` for shell scripts that run inside Linux containers",
575
+ "summary": "Under a Windows checkout with `core.autocrlf=true`, git rewrites `*.sh` to CRLF on checkout and bash chokes with `$'\\r': command not found` at line 1. Locally reproduced the OSS-Fuzz upstream submission failure (zero artifacts compiled, every `compile_javascript_fuzzer` call failed silently on the CRLF). Unblocks the OSS-Fuzz upstream submission.",
576
+ "sections": [
577
+ {
578
+ "heading": "Fixed",
579
+ "items": [
580
+ {
581
+ "title": "`.gitattributes` enforces LF endings on `*.sh`",
582
+ "body": "Adds an explicit `*.sh text eol=lf` override so every `.sh` checks out LF regardless of platform; existing tracked scripts re-normalized (CRLF stripped) in the same commit. Verified end-to-end: `docker run ... gcr.io/oss-fuzz-base/base-builder-javascript bash /src/build.sh` now compiles all 15 fuzz harnesses + zips their seed corpora cleanly."
583
+ }
584
+ ]
585
+ }
586
+ ]
587
+ },
588
+ {
589
+ "version": "0.8.73",
590
+ "date": "2026-05-10",
591
+ "headline": "ClusterFuzzLite + OSS-Fuzz integration replaces the hand-rolled fuzz harness",
592
+ "summary": "Every `fuzz/*.fuzz.js` is now a jazzer.js / libFuzzer entry-point (`module.exports.fuzz = function (data) { ... }`) so the engine drives the target with coverage-guided mutation instead of random bytes. Same 15 targets as v0.8.72. ClusterFuzzLite runs locally + on PRs; OSS-Fuzz submission-ready project config ships under `oss-fuzz/projects/blamejs/`.",
593
+ "sections": [
594
+ {
595
+ "heading": "Changed",
596
+ "items": [
597
+ {
598
+ "title": "Fuzz harnesses migrated to jazzer.js / libFuzzer",
599
+ "body": "Every `fuzz/*.fuzz.js` exports `module.exports.fuzz = function (data) { ... }`. Each gets a `fuzz/<name>_seed_corpus/` directory with realistic bootstrap inputs that libFuzzer mutates from. The shared `_expected.js` classifies operator-friendly framework throws (codes matching `<domain>/<error>` or `<domain>.<error>` shape; node-builtin error subclasses with input-shape messaging) as expected outcomes; anything else escapes as a finding the engine records + minimizes into a regression-corpus entry."
600
+ },
601
+ {
602
+ "title": "ClusterFuzzLite (local, free)",
603
+ "body": "`.clusterfuzzlite/Dockerfile` + `build.sh` + `project.yaml` ride alongside the framework source. Two GH Actions workflows wire it in — `cflite_pr.yml` runs 300s of coverage-guided fuzzing per target on every PR touching `lib/` or `fuzz/`; `cflite_batch.yml` runs the deeper 1800s batch + 600s coverage measurement on a daily 05:17 UTC schedule. Findings surface as PR annotations + SARIF in the Security tab."
604
+ },
605
+ {
606
+ "title": "OSS-Fuzz upstream-submission config",
607
+ "body": "`oss-fuzz/projects/blamejs/{Dockerfile, build.sh, project.yaml, README.md}` is the submission-ready project config that gets copy-pasted into `projects/blamejs/` in the `google/oss-fuzz` upstream repo. Once accepted, ClusterFuzz fuzzes 24/7 on Google Cloud with permanent corpus persistence, stack-trace dedup, automatic regression testing against every commit, and a public coverage dashboard. The OSS-Fuzz `build.sh` mirrors `.clusterfuzzlite/build.sh` byte-for-byte (modulo comment block) so findings reproduce identically locally."
608
+ }
609
+ ]
610
+ },
611
+ {
612
+ "heading": "Detectors",
613
+ "items": [
614
+ {
615
+ "title": "Fuzz-coverage gate now verifies jazzer.js shape",
616
+ "body": "`testParserPrimitivesHaveFuzzHarness` verifies the jazzer.js shape (`module.exports.fuzz = ...`) in addition to the missing-harness check — a future parser primitive lands either with a coverage-guided harness or an audited `FUZZ_NOT_REQUIRED` entry. `npm run fuzz` switched to invoke jazzer.js for one-target local dev (`npx @jazzer.js/core fuzz/safe-json.fuzz.js -- -max_total_time=60`); the previous random-fuzzer + standalone `.github/workflows/fuzz.yml` are removed. SECURITY.md threat-model + operator-checklist updated with the new dual-pipeline posture."
617
+ }
618
+ ]
619
+ }
620
+ ]
621
+ },
622
+ {
623
+ "version": "0.8.72",
624
+ "date": "2026-05-10",
625
+ "headline": "Fuzz harness against the parser / validator surface + smoke-time fuzz-coverage gate",
626
+ "summary": "New `fuzz/` directory ships hand-rolled fuzz harnesses against the 11 highest-value adversarial-input primitives. Each harness generates random / mutated / bidi-salted / control-char-salted inputs against a per-target seed corpus, runs until `FUZZ_BUDGET_MS` elapses, and fails with a reproducer when the target throws an unexpected error. New CI workflow + a Layer 0 detector that enforces fuzz-harness coverage for `lib/safe-*.js` and `lib/guard-*.js` files.",
627
+ "sections": [
628
+ {
629
+ "heading": "Added",
630
+ "items": [
631
+ {
632
+ "title": "`fuzz/` directory + 11 hand-rolled harnesses",
633
+ "body": "Targets: `b.safeJson.parse`, `b.safeUrl.parse`, `b.safeJsonPath.validateExpression`, `b.guardCsv.validate`, `b.guardHtml.validate`, `b.guardJson.parse`, `b.guardYaml.parse`, `b.guardXml.validate`, `b.guardSvg.validate`, `b.guardMarkdown.validate`, `b.guardEmail.validateMessage`. Each runs until `FUZZ_BUDGET_MS` elapses (default 30s; CI: 60s on PR / 300s on schedule) and fails with a reproducer when the target throws an unexpected error (vs. an operator-friendly framework error code in the documented `domain/error` or `domain.error` shape). Native `TypeError` with input-shape messaging, `SyntaxError`, and `RangeError` matching the depth/length/cap contract are accepted; everything else is a finding."
634
+ },
635
+ {
636
+ "title": "`.github/workflows/fuzz.yml`",
637
+ "body": "Runs the harness in matrix on every PR touching `lib/` or `fuzz/` and on a daily 05:17 UTC schedule. `npm run fuzz` runs every harness sequentially via `fuzz/_run-all.js` for local dev."
638
+ }
639
+ ]
640
+ },
641
+ {
642
+ "heading": "Detectors",
643
+ "items": [
644
+ {
645
+ "title": "`testParserPrimitivesHaveFuzzHarness`",
646
+ "body": "New Layer 0 detector in `test/layer-0-primitives/codebase-patterns.test.js` enforces that every `lib/safe-*.js` and `lib/guard-*.js` file has a corresponding `fuzz/<name>.fuzz.js` OR an explicit `FUZZ_NOT_REQUIRED` allowlist entry with reason — so a future parser primitive can't silently ship without fuzz coverage."
647
+ }
648
+ ]
649
+ },
650
+ {
651
+ "heading": "Fixed",
652
+ "items": [
653
+ {
654
+ "title": "README OpenSSF Scorecard badge URL",
655
+ "body": "Corrected `api.scorecards.dev` → `api.scorecard.dev` (plural-singular typo)."
656
+ }
657
+ ]
658
+ }
659
+ ]
660
+ },
661
+ {
662
+ "version": "0.8.71",
663
+ "date": "2026-05-10",
664
+ "headline": "CI green-up — cosign-installer action commit SHA correction",
665
+ "summary": "The v0.8.70 `npm-publish` workflow's cosign-sign-blob step couldn't resolve its pinned `sigstore/cosign-installer` action commit SHA because the SHA was a typo, not a real commit on the action's repo. Replaced with the actual v3.7.0 commit SHA so the publish pipeline resolves the dependency and runs end-to-end.",
666
+ "sections": [
667
+ {
668
+ "heading": "Fixed",
669
+ "items": [
670
+ {
671
+ "title": "`sigstore/cosign-installer` action SHA pin corrected",
672
+ "body": "The pinned commit SHA `d7d6e07b3e89342f1d8bcd4f76c2fa5a9d1a1f7e` did not exist on the action's repo and broke the publish workflow at the cosign-installer step. Replaced with the actual v3.7.0 commit SHA `dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da`. No primitive surface change versus v0.8.70."
673
+ }
674
+ ]
675
+ }
676
+ ]
677
+ },
678
+ {
679
+ "version": "0.8.70",
680
+ "date": "2026-05-10",
681
+ "headline": "Additive surface across OAuth/OIDC, FAPI 2.0, browser hardening, MCP safety, compliance, and supply-chain",
682
+ "summary": "Bundled additive release covering RFC 9207 iss-validation, JARM decoding, refresh-token replay defense, FAPI 2.0 message-signing posture, Private Network Access preflight handling, MCP tool-result sanitization + capability checks, five new compliance postures, the EU Data Act primitive, and supply-chain hardening (CycloneDX 1.6 + OSV-Scanner + Sigstore cosign signing).",
683
+ "sections": [
684
+ {
685
+ "heading": "Added",
686
+ "items": [
687
+ {
688
+ "title": "OAuth/OIDC: iss validation, JARM decoding, refresh-token replay defense",
689
+ "body": "`b.auth.oauth.parseCallback(query, opts?)` validates the RFC 9207 AS Issuer Identifier — refuses iss-mismatch and OP `error=` redirects; optional `requireIssParam` refuses missing iss. `parseJarmResponse(jwt, opts?)` decodes OAuth 2.0 JARM signed authorization responses. `refreshAccessToken(token, { seen })` accepts an operator-supplied callback that refuses replayed refresh tokens before any HTTP call (RFC 9700 §4.13 / OAuth 2.1 §6.1 one-time-use rotation); returns `refreshTokenRotated: true` on success."
690
+ },
691
+ {
692
+ "title": "FAPI 2.0 runtime checks + `fapi-2.0-message-signing` posture",
693
+ "body": "`b.fapi2.assertCallback(query)` refuses missing iss when `fapi-2.0` posture is set, and refuses bare-param when `fapi-2.0-message-signing` is set (requires JARM `response`). `b.fapi2.assertAuthzRequest(authzParams)` refuses non-JAR (bare-param) authorization requests under FAPI 2.0. New `fapi-2.0-message-signing` posture registered."
694
+ },
695
+ {
696
+ "title": "Browser hardening: Permissions-Policy denylist + PNA preflight + 401 cache headers",
697
+ "body": "`Permissions-Policy` defaults extend with `storage-access=()`, `browsing-topics=()`, `private-aggregation=()`, `controlled-frame=()`, `captured-surface-control=()`. `b.middleware.cors` gains an `allowPrivateNetwork` opt + Private Network Access preflight handling — refuses `Access-Control-Request-Private-Network` by default, sets `Access-Control-Allow-Private-Network: true` when opted in. `b.middleware.requireAuth` / `requireAal` / `requireStepUp` 401 responses now set `Cache-Control: no-store` per RFC 9111 §5.2.2.5."
698
+ },
699
+ {
700
+ "title": "MCP safety + LLM07/08 mitigations",
701
+ "body": "`b.mcp.toolResult.sanitize(result, opts?)` runs a prompt-injection regex + dangerous-HTML detection + URL allowlist on tool outputs (modes `refuse` / `sanitize` / `audit-only`). `b.mcp.capability.create(scopes)` + `satisfiedBy(granted)` formalize least-privilege capability checks. `b.mcp.validateToolInput(toolName, input, schema)` enforces a JSON Schema 2020-12 subset (`type` / `properties` / `required` / `items` / `enum` / `const` / `minLength` / `maxLength` / `minimum` / `maximum`)."
702
+ },
703
+ {
704
+ "title": "Compliance postures: `modpa`, `nydfs-500`, `hipaa-2026`, `quebec-25`, `fapi-2.0-message-signing`",
705
+ "body": "Maryland Online Data Privacy Act, NY DFS Cybersecurity Regulation, HHS HIPAA Final Rule effective 2026, Quebec Law 25, and the FAPI 2.0 message-signing financial posture all register in `b.compliance.KNOWN_POSTURES` with the matching cascade entries."
706
+ },
707
+ {
708
+ "title": "`b.dataAct` — EU Data Act (Regulation 2023/2854)",
709
+ "body": "`declareProduct`, `recordUserAccess`, `shareWithThirdParty` (Art 32 §1 refuses sharing with DMA designated gatekeepers without an audited override via `acceptGatekeeper.reason`), `recordSwitchRequest` (Art 28 §3 caps notice period at 30 days)."
710
+ },
711
+ {
712
+ "title": "Supply-chain hardening — SBOM bump, OSV-Scanner, Sigstore SBOM signing, dep-confusion claim",
713
+ "body": "SBOM bumped to CycloneDX 1.6. The `npm-publish` workflow now runs OSV-Scanner with `--fail-on-vuln=HIGH` and signs the SBOM via the Sigstore cosign keyless flow (attaches the `.sigstore` bundle to the GitHub release alongside the JSON). `scripts/publish-dep-confusion-placeholder.sh` claims unscoped names (`blamejs`, `blame-js`, `blamejs-core`) on npm with placeholder packages that exit 1 + redirect to canonical `@blamejs/core`; manual, run on maintainer rotation, refuses overwrite when a different owner already holds the name."
714
+ }
715
+ ]
716
+ }
717
+ ],
718
+ "references": [
719
+ {
720
+ "label": "RFC 9207 OAuth 2.0 Authorization Server Issuer Identification",
721
+ "url": "https://www.rfc-editor.org/rfc/rfc9207.html"
722
+ },
723
+ {
724
+ "label": "RFC 9700 OAuth 2.0 Security Best Current Practice",
725
+ "url": "https://www.rfc-editor.org/rfc/rfc9700.html"
726
+ },
727
+ {
728
+ "label": "RFC 9111 HTTP Caching",
729
+ "url": "https://www.rfc-editor.org/rfc/rfc9111.html"
730
+ },
731
+ {
732
+ "label": "FAPI 2.0 Security Profile",
733
+ "url": "https://openid.net/specs/fapi-2_0-security-profile.html"
734
+ },
735
+ {
736
+ "label": "EU Data Act (Regulation 2023/2854)",
737
+ "url": "https://eur-lex.europa.eu/eli/reg/2023/2854/oj"
738
+ },
739
+ {
740
+ "label": "CycloneDX 1.6",
741
+ "url": "https://cyclonedx.org/docs/1.6/json/"
742
+ }
743
+ ]
744
+ },
745
+ {
746
+ "version": "0.8.69",
747
+ "date": "2026-05-10",
748
+ "headline": "Test-side `waitUntil` helper for observable async conditions",
749
+ "summary": "Recurring `SMOKE_PARALLEL=64` and macOS-runner flakes shared one root cause — fixed-budget `setTimeout(r, N)` sleeps too short for runner-contention reality. A new polling helper replaces hand-tuned sleeps with assertions against the observable condition itself, exiting early on fast platforms and using the full budget on contended ones.",
750
+ "sections": [
751
+ {
752
+ "heading": "Added",
753
+ "items": [
754
+ {
755
+ "title": "`waitUntil` + `waitUntilEqual` test helpers",
756
+ "body": "`test/helpers/wait.js` ships `waitUntil(predicate, opts?)` — polls every `intervalMs` (default 25ms) up to `timeoutMs` (default 5000ms), exits early when the predicate returns truthy, throws a labeled error on timeout. `waitUntilEqual(getter, expected)` is the convenience wrapper for the common case. Both re-exported from `test/helpers/index.js`."
757
+ }
758
+ ]
759
+ },
760
+ {
761
+ "heading": "Changed",
762
+ "items": [
763
+ {
764
+ "title": "`log-stream-otlp` collector-retry gate converted to `waitUntil`",
765
+ "body": "`test/layer-0-primitives/log-stream-otlp.test.js`'s 'collector saw retries' gate now uses `waitUntil({ failCount >= 2, dropEvents.length === 1 })` instead of `_sleep(200)`. Fast platforms exit in ~30ms; contended platforms get the full 5s budget. The general convention: when you find yourself bumping a hand-tuned sleep to fix a CI flake, that's the smell — convert to `waitUntil` so future flake fixes adjust one timeout ceiling instead of N inline budgets."
766
+ }
767
+ ]
768
+ }
769
+ ]
770
+ },
771
+ {
772
+ "version": "0.8.68",
773
+ "date": "2026-05-10",
774
+ "headline": "`b.watcher` polling backend for environments where `fs.watch` doesn't deliver events",
775
+ "summary": "Pre-v0.8.68 the watcher used `fs.watch(root, { recursive: true })` exclusively — silent on filesystems where the kernel→userspace event bridge doesn't exist: Docker Desktop bind-mounts on Windows / macOS hosts (gRPC-FUSE / VirtioFS), NFS / SMB, some FUSE filesystems. Adds `mode: \"fs\" | \"poll\"` (default `\"fs\"`) — when `\"poll\"`, the watcher walks the tree on a fixed interval and diffs against the previous snapshot.",
776
+ "sections": [
777
+ {
778
+ "heading": "Added",
779
+ "items": [
780
+ {
781
+ "title": "`b.watcher.create({ mode: \"poll\" })` polling backend",
782
+ "body": "When `mode: \"poll\"`, the watcher walks the tree on a fixed interval and diffs against the previous snapshot. New file / mtime-change / size-change → `onChange` via the existing debounce + ignore + lstat dispatch; missing path → `onDelete`. `pollIntervalMs` (default 1s) sets cadence; `pollMaxFiles` (default 50000) caps the per-tick walk so a misconfigured root can't stall the event loop stat'ing 100k files every second — overflow refuses with `watcher/poll-overflow`. Symlinks skipped (matches `fs.watch` path). The initial walk happens synchronously in `create()` so the first event fires only on real post-start changes (not on pre-existing files). `_flushForTest()` runs one synchronous tick + drains pending debounces so polling tests don't have to sleep `pollIntervalMs`. Returned handle gains `.mode` for operator introspection. `fs.watch`-backend error messages now suggest `mode: \"poll\"` as the fallback."
783
+ }
784
+ ]
785
+ }
786
+ ]
787
+ },
788
+ {
789
+ "version": "0.8.67",
790
+ "date": "2026-05-10",
791
+ "headline": "SAML XMLDSig Reference Transforms (`enveloped-signature` + per-Reference c14n) + IdP round-trip in the federation-auth test",
792
+ "summary": "Pre-v0.8.67 `b.auth.saml.sp.verifyResponse` only honored the SignedInfo's `CanonicalizationMethod`; it didn't process the `<ds:Transforms>` block on the Reference. Real-world IdP-signed responses (Keycloak, ADFS, Okta) attach `http://www.w3.org/2000/09/xmldsig#enveloped-signature` + a per-Reference `xml-exc-c14n#` Transform; without them, the digest never matched and verifyResponse rejected legitimate responses. No primitive surface change versus v0.8.66.",
793
+ "sections": [
794
+ {
795
+ "heading": "Fixed",
796
+ "items": [
797
+ {
798
+ "title": "`_verifyXmldsig` honors per-Reference Transforms",
799
+ "body": "Reads the Transforms list, applies `enveloped-signature` by filtering the parsed-tree's `<Signature>` element children before canonicalization, and honors the per-Reference c14n choice (with vs without comments). The single-match-by-ID invariant + signature-wrapping defense moves into the saml.js path directly so the modified subtree (signature stripped) is the one that gets canonicalized + digested. Unsupported Transform algorithms refuse loudly via `auth-saml/unsupported-transform`."
800
+ },
801
+ {
802
+ "title": "Full IdP-emitted SAML round-trip in the federation-auth integration test",
803
+ "body": "`test/integration/federation-auth.test.js` now drives Keycloak's HTML login form via cookie-jar curl-equivalent (no headless browser needed), captures the IdP-signed SAMLResponse, fetches the IdP signing certificate from `/protocol/saml/descriptor`, hands the response to `sp.verifyResponse(b64, { expectedInResponseTo })`, and asserts the extracted `nameId` / `issuer` / `audience` / `inResponseTo` match the realm's signed claims."
804
+ }
805
+ ]
806
+ }
807
+ ]
808
+ },
809
+ {
810
+ "version": "0.8.66",
811
+ "date": "2026-05-10",
812
+ "headline": "`b.session.updateData(token, data, opts?)`",
813
+ "summary": "Update the sealed `data` payload on a session WITHOUT rotating the sid. Pre-v0.8.66 the only path to mutate session data was `b.session.rotate(token, { data })` which forces an sid rotation — appropriate for security-boundary transitions (login, MFA, role escalation) but heavyweight for cart-state writes / preference flips / step-up-completion flags.",
814
+ "sections": [
815
+ {
816
+ "heading": "Added",
817
+ "items": [
818
+ {
819
+ "title": "`b.session.updateData(token, data, opts?)`",
820
+ "body": "Default semantics: full payload replace, `lastActivity` bumped (idle-timeout reset), reserved `__bj_fingerprint` binding preserved automatically so `verify()` still surfaces drift correctly. `opts.merge: true` does a one-level deep merge into the existing payload; `opts.touchLastActivity: false` skips the idle-timeout bump. Returns `false` for unknown / expired / pre-v0.8.61-raw-format tokens (no throw). Anonymous-session userIds work the same as named userIds. Leader-only."
821
+ }
822
+ ]
823
+ }
824
+ ]
825
+ },
826
+ {
827
+ "version": "0.8.65",
828
+ "date": "2026-05-10",
829
+ "headline": "Federated-authentication integration test fixture (Keycloak as OIDC OP + SAML IdP)",
830
+ "summary": "Adds `quay.io/keycloak/keycloak:26.0` to `docker-compose.test.yml` with realm-import on ports :18080 (HTTP) + :18081 (Quarkus health). Realm boots with one OIDC client, one SAML SP client, and a test user. New `test/integration/federation-auth.test.js` exercises end-to-end OIDC + SAML flows against the live Keycloak.",
831
+ "sections": [
832
+ {
833
+ "heading": "Added",
834
+ "items": [
835
+ {
836
+ "title": "Keycloak integration test fixture",
837
+ "body": "Adds `quay.io/keycloak/keycloak:26.0` to `docker-compose.test.yml` running with realm-import on port `:18080` (HTTP) + `:18081` (Quarkus health). Realm `blamejs-test` boots with one OIDC client (`blamejs-rp-oidc`, secret `blamejs-test-rp-secret`, frontchannel + backchannel logout enabled), one SAML SP client (entityID `https://sp.blamejs-test.example`, RSA-SHA256 assertion signature), and a test user (`alice` / `blamejs-test-password`)."
838
+ },
839
+ {
840
+ "title": "`test/integration/federation-auth.test.js`",
841
+ "body": "Exercises end-to-end against the live Keycloak: OIDC discovery, `b.auth.oauth.authorizationUrl` (state + nonce + PKCE), password-grant token retrieval, `verifyIdToken` against the realm JWKS, `fetchUserInfo` with `idTokenSub` cross-check, RP-Initiated Logout URL build, `parseFrontchannelLogoutRequest` iss-mismatch refusal, `verifyBackchannelLogoutToken` JWKS-lookup + signature + typ-check failure paths, SAML `buildAuthnRequest` POST to the IdP's `/protocol/saml` endpoint, SP `metadata()` XML emit, and CIBA `startAuthentication` wire-format check (Keycloak's `backchannel_authentication_endpoint` is at `/protocol/openid-connect/ext/ciba/auth`)."
842
+ }
843
+ ]
844
+ },
845
+ {
846
+ "heading": "Fixed",
847
+ "items": [
848
+ {
849
+ "title": "`b.auth.ciba._postForm` response handling + URL validation",
850
+ "body": "Sets `responseMode: \"always-resolve\"` so deterministic OAuth-shape error JSON (`{ error, error_description }`) reaches the AuthError-mapping path instead of being rejected as a generic HTTP error. URL validation switched from a non-existent `safeUrl.assertHttpUrl` to `safeUrl.parse({ allowedProtocols })`."
851
+ },
852
+ {
853
+ "title": "Service-check + helper wiring for Keycloak",
854
+ "body": "`scripts/check-services.js` registers `keycloak` + `keycloak-health` (both v4 + v6); `test/helpers/services.js` exposes `URLS.keycloak`. The release workflow documents the federation-auth integration test path under live-integration gates. OID4VCI / OID4VP / OpenID Federation deferred — Keycloak's `oid4vc-issuer` SPI is preview-only and there's no entity-statement publisher in the base image."
855
+ }
856
+ ]
857
+ }
858
+ ]
859
+ },
860
+ {
861
+ "version": "0.8.64",
862
+ "date": "2026-05-10",
863
+ "headline": "CI green-up for v0.8.63 (wiki source-comment validator)",
864
+ "summary": "v0.8.62 missed `examples/wiki/test/validate-source-comment-blocks.js` — a wiki-side validator that enforces every `@primitive` block has `@signature` starting with `b.`, matching function arity, an `@opts` declaration when the function takes opts, an `@example` block, and resolvable `@related` cross-refs. 50 findings across the new federation/VC primitives. No primitive surface change versus v0.8.63.",
865
+ "sections": [
866
+ {
867
+ "heading": "Fixed",
868
+ "items": [
869
+ {
870
+ "title": "Wiki source-comment validator findings on federation/VC primitives",
871
+ "body": "Every nested-namespace `@signature` (`b.auth.ciba.client.X`, `b.auth.oid4vci.issuer.X`, `b.auth.oid4vp.verifier.X`, `b.auth.saml.sp.X`) re-qualified with the full `b.*` prefix instead of the bare `client.X` shorthand. Arity collapsed to `(opts)` where the function takes a single opts arg. `@example` block added to every primitive. `@opts` block added to `ciba.client.startAuthentication` / `parseNotification` + `openidFederation.resolveLeaf`. `@primitive` block added to `xmlC14n.parse`. `@related` cross-refs scrubbed of dangling references to undocumented primitives. The parse-as-JS check on oid4vci's `@example` caught a `{...}` literal with no target — replaced with a concrete object spread."
872
+ }
873
+ ]
874
+ }
875
+ ]
876
+ },
877
+ {
878
+ "version": "0.8.63",
879
+ "date": "2026-05-10",
880
+ "headline": "CI green-up for v0.8.62 (ESLint findings on the new federation/VC primitives)",
881
+ "summary": "Ten ESLint findings missed by the local pre-ship audit (eslint not run before commit): unused vars, an unnecessary `\\-` escape in xml-c14n's name-character regex, and a control-character regex in CIBA's binding_message validator (`no-control-regex` refuses control-char ranges in regex literals regardless of `\\u` escaping). No primitive surface change versus v0.8.62.",
882
+ "sections": [
883
+ {
884
+ "heading": "Fixed",
885
+ "items": [
886
+ {
887
+ "title": "Unused vars + redundant regex escape across federation/VC primitives",
888
+ "body": "Removed `openTag` in xml-c14n; `cache` + unused `C` import + `DEFAULT_CHAIN_TTL_MS` in openid-federation; `safeJson` in oid4vp; `sdJwtVcCore` + `SUPPORTED_PROOF_TYPES` in oid4vci. Dropped a redundant `\\-` escape in xml-c14n's name-character regex."
889
+ },
890
+ {
891
+ "title": "CIBA `binding_message` control-char scan",
892
+ "body": "Replaced the regex with an explicit codepoint scan in `_validateBindingMessage` that walks `msg.charCodeAt(i)` and refuses C0 / DEL+C1 / zero-width / bidi-mark / bidi-isolate / BOM ranges (`no-control-regex` refuses control-char ranges in regex literals)."
893
+ }
894
+ ]
895
+ }
896
+ ]
897
+ },
898
+ {
899
+ "version": "0.8.62",
900
+ "date": "2026-05-10",
901
+ "headline": "Federation / VC primitive family + standalone DB-file lifecycle + anonymous-session ergonomics",
902
+ "summary": "OpenID Connect Front-Channel + Back-Channel Logout, CIBA Core 1.0, OpenID4VCI 1.0, OpenID4VP 1.0 + DCQL, OpenID Federation 1.0, SAML 2.0 SP, RFC 3741 Exclusive XML Canonicalization, SD-JWT VC key-attestation extension, `b.db.fileLifecycle` for consumers owning their own SQLite handle, anonymous-session ergonomics, and a `makeNamespacedEmitters` helper.",
903
+ "sections": [
904
+ {
905
+ "heading": "Added",
906
+ "items": [
907
+ {
908
+ "title": "OIDC Front-Channel + Back-Channel Logout 1.0 on `b.auth.oauth`",
909
+ "body": "`parseFrontchannelLogoutRequest(req)` validates iss + sid query params. `verifyBackchannelLogoutToken(jwt, vopts)` verifies the JWS (typ=logout+jwt), validates the events claim per OIDC §2.6, refuses logout-tokens carrying nonce, requires sub OR sid, and runs an operator-supplied `seen({ jti, iss, iat })` callback for jti-replay defense. Discovery surface extended to `check_session_iframe` + `backchannel_authentication_endpoint`."
910
+ },
911
+ {
912
+ "title": "CIBA Core 1.0 at `b.auth.ciba.client.create`",
913
+ "body": "`deliveryMode: \"poll\"|\"ping\"|\"push\"` with `startAuthentication` / `pollToken` / `parseNotification`. Supports JWT-bearer / mTLS / shared-secret client auth, binding_message + acr_values + requested_expiry, ping/push notification token (timing-safe compared via sha3 hash), and the `urn:openid:params:grant-type:ciba` grant."
914
+ },
915
+ {
916
+ "title": "OpenID4VCI 1.0 at `b.auth.oid4vci.issuer.create`",
917
+ "body": "Issuer-initiated credential_offer with pre-authorized_code + tx_code, /token grant exchange, /credential endpoint with proof-JWT verification (typ=openid4vci-proof+jwt, iat freshness, nonce-replay defense, holder-key binding via header.jwk + JWS verify), c_nonce rotation per request, /.well-known/openid-credential-issuer metadata. Composes `b.auth.sdJwtVc.issuer` for SD-JWT VC minting."
918
+ },
919
+ {
920
+ "title": "OpenID4VP 1.0 + DCQL at `b.auth.oid4vp.verifier.create`",
921
+ "body": "`createRequest` builds a vp_token authz request with a DCQL query. `verifyResponse` parses the wallet's vp_token, runs each presentation through `b.auth.sdJwtVc.verify` (audience + nonce + KB-JWT bound, optional key_attestation verifier), then runs the DCQL matcher. DCQL covers credentials[] (id / format / meta vct_values / meta issuer_values / claims with path + values) and credential_sets[] (options + required) per OID4VP §6."
922
+ },
923
+ {
924
+ "title": "OpenID Federation 1.0",
925
+ "body": "`b.auth.openidFederation.{ parseEntityStatement, verifyEntityStatement, buildTrustChain, applyMetadataPolicy, resolveLeaf }` — fetches entity configs from `<entity>/.well-known/openid-federation`, walks `authority_hints` up to a trust anchor (operator-pinned JWKS), verifies each subordinate-statement JWS, and applies the federation's metadata_policy (`value` / `default` / `add` / `one_of` / `subset_of` / `superset_of` / `essential` — unknown operators refuse)."
926
+ },
927
+ {
928
+ "title": "SAML 2.0 SP at `b.auth.saml.sp.create`",
929
+ "body": "AuthnRequest builder for HTTP-Redirect / POST bindings. Response parser verifies XMLDSig (Response-level OR Assertion-level signature), defends against signature-wrapping via `b.xmlC14n.canonicalizeElementById`'s single-match invariant, validates SubjectConfirmation Bearer (NotOnOrAfter / NotBefore / Recipient / InResponseTo) + Conditions audience + Status. Plus `b.auth.saml.fetchMdq({ baseUrl, entityId, trustCertPem? })` for MDQ-style metadata fetch with optional XMLDSig verification."
930
+ },
931
+ {
932
+ "title": "`b.xmlC14n` — RFC 3741 Exclusive XML Canonicalization",
933
+ "body": "`canonicalize(input, opts?)` and `canonicalizeElementById(xml, id, opts?)`. The `canonicalizeElementById` single-match invariant is the core defense against XML signature-wrapping attacks (refuses ID collisions + zero-match references). Doctype + ENTITY refused at parse time."
934
+ },
935
+ {
936
+ "title": "SD-JWT VC `key_attestation` extension",
937
+ "body": "`b.auth.sdJwtVc.holder.store({..., keyAttestation })` persists a holder-side attestation JWT alongside the credential. `b.auth.sdJwtVc.present({..., keyAttestation })` embeds it in the KB-JWT header. `b.auth.sdJwtVc.verify(presentation, { keyAttestationVerifier, requireKeyAttestation })` surfaces the attestation token to an operator-supplied verifier so trust-anchor decisions (TEE / FIDO MDS3 / App Attest / Play Integrity) stay operator-side."
938
+ },
939
+ {
940
+ "title": "`b.db.fileLifecycle({ dataDir, vault, ... })`",
941
+ "body": "Standalone encrypted-DB-file lifecycle for consumers that own their own SQLite handle (own schema, own migrations, own connection). Decrypts `<dataDir>/db.enc` to a tmpfs path (`/dev/shm` on Linux), exposes `dbPath` for the operator to open, runs a periodic re-encrypt flush via `startFlushTimer(db)`, returns an in-memory snapshot via `snapshot(db)`, and runs a graceful flush + cleanup via `flushAndCleanup(db, opts)`. Same envelope shape as `b.db`; no schema / audit-chain coupling."
942
+ },
943
+ {
944
+ "title": "`b.session.create({ anonymous: true })`",
945
+ "body": "Auto-mints `userId = \"anon:\" + crypto.randomUUID()` so operators running pre-login flows keep the full sealed-cookie + sealed-userId + sidHash + idle/absolute-timeout posture without rolling their own opaque-id pattern. `b.session.isAnonymous(userId)` helper for post-auth gates. `destroyAllForUser` refuses anon-prefix ids (per-session, not portable)."
946
+ },
947
+ {
948
+ "title": "`validateOpts.makeNamespacedEmitters(prefix, { audit, observability })`",
949
+ "body": "Collapses the recurring per-primitive `_emitAudit` / `_emitMetric` boilerplate into one call; the new federation/VC primitives consume it."
950
+ }
951
+ ]
952
+ }
953
+ ],
954
+ "references": [
955
+ {
956
+ "label": "OpenID Connect Front-Channel Logout 1.0",
957
+ "url": "https://openid.net/specs/openid-connect-frontchannel-1_0.html"
958
+ },
959
+ {
960
+ "label": "OpenID Connect Back-Channel Logout 1.0",
961
+ "url": "https://openid.net/specs/openid-connect-backchannel-1_0.html"
962
+ },
963
+ {
964
+ "label": "OpenID Client-Initiated Backchannel Authentication 1.0",
965
+ "url": "https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html"
966
+ },
967
+ {
968
+ "label": "OpenID4VCI",
969
+ "url": "https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html"
970
+ },
971
+ {
972
+ "label": "OpenID4VP",
973
+ "url": "https://openid.net/specs/openid-4-verifiable-presentations-1_0.html"
974
+ },
975
+ {
976
+ "label": "OpenID Federation 1.0",
977
+ "url": "https://openid.net/specs/openid-federation-1_0.html"
978
+ },
979
+ {
980
+ "label": "RFC 3741 Exclusive XML Canonicalization",
981
+ "url": "https://www.rfc-editor.org/rfc/rfc3741.html"
982
+ }
983
+ ]
984
+ },
985
+ {
986
+ "version": "0.8.61",
987
+ "date": "2026-05-10",
988
+ "headline": "PQC-sealed session cookie + IP-prefix fingerprint binding + pluggable session store + `b.db.collection` schemaless extensions",
989
+ "summary": "Breaking pre-1.0 change: `b.session.create(...).token` now returns a vault-sealed envelope (ML-KEM-1024 + P-384 hybrid + XChaCha20-Poly1305) instead of the plaintext sid. Pre-v0.8.61 raw-sid cookies fail to unseal and the affected user re-authenticates. Adds `b.db.collection` schemaless-document opts (`overflow: \"data\"`, `jsonColumns`, `sealedFields`), IPv4 /24 + IPv6 /64 fingerprint binding for roaming carriers, and a pluggable session store with a first-party `localDbThin` adapter.",
990
+ "sections": [
991
+ {
992
+ "heading": "Changed",
993
+ "items": [
994
+ {
995
+ "title": "BREAKING: `b.session.create(...).token` returns a vault-sealed envelope",
996
+ "body": "Token is now a `vault:`-prefixed ML-KEM-1024 + P-384 hybrid + XChaCha20-Poly1305 envelope instead of the plaintext sid. Pre-v0.8.61 raw-sid cookies fail to unseal at `b.session.verify` and the affected user re-authenticates. Pre-v1.0 ships no compat shim — every existing session force-logs-out on upgrade. Storage path unchanged: the DB still keys on `sha3('bj-session:' || sid)`, sealing only changes the wire format."
997
+ },
998
+ {
999
+ "title": "Session fingerprint subnet binding",
1000
+ "body": "`fingerprintFields: [\"clientIpPrefix\"]` hashes the client IP at `/24` for IPv4 and `/64` for IPv6 (per RFC 4291 §2.5.4 customer-LAN allocation), so roaming carriers' per-request IP flips no longer log out healthy mobile users. IPv4-mapped IPv6 (`::ffff:1.2.3.4`) buckets as v4 `/24`."
1001
+ }
1002
+ ]
1003
+ },
1004
+ {
1005
+ "heading": "Added",
1006
+ "items": [
1007
+ {
1008
+ "title": "`b.db.collection` schemaless-document extensions",
1009
+ "body": "`{ overflow: \"data\" }` folds every insert/update field outside the table's column list into a JSON-text column; `find` / `findOne` parse it and merge keys back onto the row. `WHERE` on an unknown field rewrites to `JSON_EXTRACT(<overflow>, '$.field')` for `$eq` / `$ne` / `$in` (range / `$like` require a real column with an index). `{ jsonColumns: [\"roles\", \"metadata\"] }` auto-`JSON.stringify`s listed columns on write and parses them via `b.safeJson` on read; unknown columns refuse at first use. `{ sealedFields: { email: \"emailHash\" } }` co-locates sealed-column / derived-hash declarations with the collection — registers via `b.cryptoField.registerTable` so the existing query-builder sealed-field rewrite picks up automatically."
1010
+ },
1011
+ {
1012
+ "title": "Pluggable session store + `localDbThin` adapter",
1013
+ "body": "`b.session.useStore(store)` swaps the `_blamejs_sessions` storage backend. First-party adapter `b.session.stores.localDbThin({ file })` wraps `b.localDb.thin` (typically pointed at tmpfs) so heavy session churn doesn't fight the main DB's WAL fsync + at-rest re-encryption cycle. `audit.FRAMEWORK_NAMESPACES` extended with `localdb` so `b.localDb.thin` open / close events stop dropping."
1014
+ }
1015
+ ]
1016
+ }
1017
+ ]
1018
+ },
1019
+ {
1020
+ "version": "0.8.60",
1021
+ "date": "2026-05-09",
1022
+ "headline": "CI green-up for v0.8.59 (`watcher.test.js` macOS structural fix)",
1023
+ "summary": "macOS smoke runner failed `watcher.test.js: surface onChange for non-ignored file` again under SMOKE_PARALLEL=64 + GitHub-Actions-runner contention; the prior 300ms→1500ms bump wasn't enough on a contended Darwin runner. Replaced the fixed-budget wait with a structural priming-then-poll loop. No primitive surface change.",
1024
+ "sections": [
1025
+ {
1026
+ "heading": "Fixed",
1027
+ "items": [
1028
+ {
1029
+ "title": "Structural prime-then-poll fix for `watcher.test.js` macOS flake",
1030
+ "body": "Two structural fixes: (a) a 200ms priming wait BEFORE the test writes any files, so the FSEvents watcher is fully established before file-system activity races its startup; (b) a poll-until-event loop after writes that flushes + checks the change set every 100ms up to a 5s deadline, exiting early when the target event lands. Linux / Windows still finish in <100ms; the wider budget only matters on contended macOS."
1031
+ }
1032
+ ]
1033
+ }
1034
+ ]
1035
+ },
1036
+ {
1037
+ "version": "0.8.59",
1038
+ "date": "2026-05-09",
1039
+ "headline": "Comment cleanup — external consumer names stripped from `lib/`",
1040
+ "summary": "Two stale references to an external consumer name inside `lib/db-collection.js` and `lib/template.js` comments stripped — names of downstream consumers don't belong in framework source. No primitive surface change versus v0.8.58.",
1041
+ "sections": [
1042
+ {
1043
+ "heading": "Changed",
1044
+ "items": [
1045
+ {
1046
+ "title": "Stripped external-consumer name references from `lib/db-collection.js` and `lib/template.js`",
1047
+ "body": "Cleanup of two stale references in source comments. Operator-facing framework source should not name downstream consumers."
1048
+ }
1049
+ ]
1050
+ }
1051
+ ]
1052
+ },
1053
+ {
1054
+ "version": "0.8.58",
1055
+ "date": "2026-05-09",
1056
+ "headline": "`b.db` query-builder + Mongo facade + `db.init` opt-outs + snapshot helper + mTLS path fix",
1057
+ "summary": "Query atoms (`.increment`, `.whereGroup`, `.orWhere`, `.search`, `.paginate`), a Mongo-shape facade at `b.db.collection(name)`, `db.init` opt-outs (`frameworkTables: false`, `auditSigning: false`), path overrides (`encryptedDbPath`, `encryptedDbName`, `dbKeyPath`), a `b.db.snapshot()` in-memory encrypted Buffer, and a `b.mtlsCa.create({ paths })` absolute-path fix. Release-workflow runtime gates rewritten so host + container phases run sequentially.",
1058
+ "sections": [
1059
+ {
1060
+ "heading": "Added",
1061
+ "items": [
1062
+ {
1063
+ "title": "Query atoms on `b.db.from(table)`",
1064
+ "body": "`.increment(column, delta)` (atomic `UPDATE col = COALESCE(col, 0) + ?`, refuses unconditional, returns rows-changed). `.whereGroup(qb => ...)` (closure-form OR composition via new `WhereBuilder` class with `.eq` / `.neq` / `.gt` / `.gte` / `.lt` / `.lte` / `.in` / `.like` AND vs `.orEq` / `.orNeq` / ... OR + `.raw`). `.orWhere(...)` (top-level OR; accepts object map / `(field, value)` / `(field, op, value)` / closure). `.search(fields, term, opts?)` (chainable LIKE-OR with `match: \"substring\"|\"prefix\"|\"exact\"`, `~` ESCAPE char to safely handle user-supplied `%`/`_`). `.paginate(opts)` (returns `{ items, total, limit, offset, page, totalPages }`; default limit 25, cap 1000)."
1065
+ },
1066
+ {
1067
+ "title": "Mongo-shape facade at `b.db.collection(name)`",
1068
+ "body": "Returns `{ insert, insertMany, find, findOne, update, updateMany, remove, count, paginate }` — maps Mongo-shape calls onto `b.db.from(name)`. Update operators: `$set` / `$inc` (composes `Query.increment`) / `$unset`. Query operators: `$eq` / `$ne` / `$gt` / `$gte` / `$lt` / `$lte` / `$in` / `$like`. Unknown operators throw at config-time."
1069
+ },
1070
+ {
1071
+ "title": "`db.init` opt-outs",
1072
+ "body": "`frameworkTables: false` skips provisioning `audit_log` / `consent_log`. `auditSigning: false` (finer-grained — keep framework tables but skip the audit-signing-key bootstrap when the host manages its own signing key)."
1073
+ },
1074
+ {
1075
+ "title": "`db.init` path overrides",
1076
+ "body": "`encryptedDbPath` (fully-qualified path to `db.enc`), `encryptedDbName` (basename under `dataDir`, default `\"db.enc\"`), `dbKeyPath` (encryption-key file outside `dataDir`, e.g. KMS-fronted volume)."
1077
+ },
1078
+ {
1079
+ "title": "`b.db.snapshot()`",
1080
+ "body": "In-memory encrypted Buffer (same envelope `flushToDisk` writes, just held in memory; WAL checkpoint forced first so committed state captures cleanly). Plain mode returns the raw plaintext SQLite file."
1081
+ }
1082
+ ]
1083
+ },
1084
+ {
1085
+ "heading": "Fixed",
1086
+ "items": [
1087
+ {
1088
+ "title": "`b.mtlsCa.create({ paths })` absolute-path",
1089
+ "body": "`_resolvePaths` no longer joins absolute path entries under `dataDir`. The pre-v0.8.58 shape silently rewrote `MTLS_CA_KEY=/etc/ssl/ca.key` → `<dataDir>/etc/ssl/ca.key`, breaking operators with externally-mounted CA files. Standard `path.join` semantics already preserve absolute arguments — the always-join was an oversight. Relative entries still join under `dataDir` (back-compat)."
1090
+ },
1091
+ {
1092
+ "title": "Release-workflow runtime-gate ordering",
1093
+ "body": "Rewritten so host smoke + host wiki e2e run BEFORE container smoke + container wiki e2e, sequentially, never in parallel. Both runners write to `.test-output/smoke.log` and `.test-output/wiki-e2e.log`; parallel runs clobber each other so the log of the actually-blocking failure may be overwritten by whichever leg finishes second. The container leg writes to `smoke-container.log` / `wiki-e2e-container.log` so diagnose-after-failure is one file lookup."
1094
+ }
1095
+ ]
1096
+ }
1097
+ ]
1098
+ },
1099
+ {
1100
+ "version": "0.8.57",
1101
+ "date": "2026-05-09",
1102
+ "headline": "CI green-up for v0.8.56 (prepack-guard negation-rule handling)",
1103
+ "summary": "The v0.8.56 npm-publish workflow's smoke gate passed but `scripts/check-pack-against-gitignore.js` rejected the tarball: it ran `git check-ignore -v` against every packed path and treated EVERY matching gitignore line as \"ignored\", including `!`-prefixed negation rules. The newly-tracked `lib/vendor/bimi-trust-anchors.pem` matches the `!lib/vendor/*.pem` negation rule that v0.8.54 added, but the script flagged it as \"gitignored in tarball\" and exited 1. No primitive surface change.",
1104
+ "sections": [
1105
+ {
1106
+ "heading": "Fixed",
1107
+ "items": [
1108
+ {
1109
+ "title": "`scripts/check-pack-against-gitignore.js` handles `!` negation rules",
1110
+ "body": "Filter out lines whose matching pattern starts with `!` — those are negation rules indicating the file is NOT actually ignored."
1111
+ }
1112
+ ]
1113
+ }
1114
+ ]
1115
+ },
1116
+ {
1117
+ "version": "0.8.56",
1118
+ "date": "2026-05-09",
1119
+ "headline": "CI green-up for v0.8.55 (watcher.test.js macOS delay)",
1120
+ "summary": "macOS smoke runner failed `watcher.test.js: surface onChange for non-ignored file`; the test bumped from 80ms→300ms in v0.8.52, but macOS `fs.watch` event delivery under `SMOKE_PARALLEL=64` + CI runner contention can still exceed 300ms. Bumped to 1500ms. Linux/Windows continue to pass on the first event-loop turn. No primitive surface change.",
1121
+ "sections": [
1122
+ {
1123
+ "heading": "Fixed",
1124
+ "items": [
1125
+ {
1126
+ "title": "`watcher.test.js` macOS delay bumped to 1500ms",
1127
+ "body": "Bumped all four delay sites to 1500ms. The longer budget only matters when macOS is contended."
1128
+ }
1129
+ ]
1130
+ }
1131
+ ]
1132
+ },
1133
+ {
1134
+ "version": "0.8.55",
1135
+ "date": "2026-05-09",
1136
+ "headline": "CI green-up for v0.8.54 (rate-limit-cluster microtask budget)",
1137
+ "summary": "The v0.8.54 npm-publish hit `rate-limit-cluster.test.js: cluster: 4th request blocked with 429` failing under CI contention. The test's `_waitMicrotasks(3)` helper chained 3 `setImmediate` ticks between `fire()` calls, but the cluster-backend's async `take()` against the DB hadn't finished bumping the counter — the 4th `fire()` read a stale count. Bumped to 20 ticks. No primitive surface change.",
1138
+ "sections": [
1139
+ {
1140
+ "heading": "Fixed",
1141
+ "items": [
1142
+ {
1143
+ "title": "`rate-limit-cluster.test.js` microtask budget",
1144
+ "body": "Bumped `_waitMicrotasks(3)` to `_waitMicrotasks(20)` across every call site. Linux/Alpine container runners pass on the first attempt; the budget only matters under `SMOKE_PARALLEL=64` contention."
1145
+ }
1146
+ ]
1147
+ }
1148
+ ]
1149
+ },
1150
+ {
1151
+ "version": "0.8.54",
1152
+ "date": "2026-05-09",
1153
+ "headline": "CI green-up for v0.8.53 (vendored BIMI PEM `.gitignore` exception)",
1154
+ "summary": "The v0.8.53 push tripped `configDrift.verifyVendorIntegrity` because `lib/vendor/bimi-trust-anchors.pem` was excluded by the global `*.pem` rule in `.gitignore` (designed to block accidental commit of operator-private keys). CI checked out a tree without the PEM, the integrity gate failed, and npm-publish never ran. No primitive surface change versus v0.8.53.",
1155
+ "sections": [
1156
+ {
1157
+ "heading": "Fixed",
1158
+ "items": [
1159
+ {
1160
+ "title": "`.gitignore` exception for vendored PEM / CRT bundles",
1161
+ "body": "Adds an explicit `!lib/vendor/*.pem` / `!lib/vendor/*.crt` exception with a comment documenting why (vendored cert bundles are public framework assets, not operator-private material). The BIMI Trust Anchor PEM is now tracked."
1162
+ }
1163
+ ]
1164
+ }
1165
+ ]
1166
+ },
1167
+ {
1168
+ "version": "0.8.53",
1169
+ "date": "2026-05-09",
1170
+ "headline": "Browser hardening + WebAuthn / FIDO + PSL substrate + email auth/transport + DNS + TLS + HTTP cache + SigV4 response headers",
1171
+ "summary": "Ships a substantial additive surface: browser headers (Clear-Site-Data, NEL, Speculation-Rules, Document-Policy, fenced-frame-src, Accept-CH / Critical-CH, Permissions-Policy structured-fields), WebAuthn L3 BE/BS flags + extension builders + conditional UI + FIDO MDS3 verifier, vendored Public Suffix List substrate (10,207 rules), DMARCbis psd= / np=, RFC 9091 BIMI VMC + CMC + Tiny-PS SVG profile, ARC trust-eval, RFC 5965 ARF ingest, iprev / FCrDNS verifier, RFC 3030 BDAT / CHUNKING / BINARYMIME, RFC 1870 SIZE pre-MAIL-FROM, RFC 3461/3464 DSN + RFC 3798/8098 MDN + RFC 2369/2919 list headers, RFC 9460 SVCB / HTTPS RR + RFC 7858 DoT + RFC 9462 DDR + RFC 9463 DNR, ECH outbound from SVCB, RFC 9525 strict PKIX server identity, full RFC 9111 outbound HTTP cache, S3 SigV4 `responseHeaders` opt on presigned URLs.",
1172
+ "sections": [
1173
+ {
1174
+ "heading": "Added",
1175
+ "items": [
1176
+ {
1177
+ "title": "Browser hardening primitives + headers",
1178
+ "body": "`b.middleware.clearSiteData` (RFC 9527 logout-side state wipe — cookies / storage / cache / executionContexts / clientHints + wildcard). `b.middleware.nel` (W3C Network Error Logging emitter — emits `NEL` + `Report-To` headers, https-only collector, CR/LF/NUL injection refusal). `b.middleware.speculationRules` (W3C Speculation Rules emitter, header form by default + `inline: true` for inline `<script type=\"speculationrules\">`). `Document-Policy` header default in `b.middleware.securityHeaders` (`document-write=?0, unsized-media=?0, oversized-images=?0`) plus operator override. CSP3 `fenced-frame-src 'none'` baked into `DEFAULT_CSP`. `Accept-CH` + `Critical-CH` UA-CH retry handshake opts. RFC 9651 Permissions-Policy structured-fields validation at config-time. `b.static` `forceAttachmentForNonText: true` opt — every served file outside the safe-render allowlist gets `Content-Disposition: attachment` + `nosniff`."
1179
+ },
1180
+ {
1181
+ "title": "WebAuthn L3 + FIDO MDS3",
1182
+ "body": "WebAuthn L3 §6.1.3 BE/BS flag surface (`backupEligible` + `backupState` on verify-returns). `b.auth.passkey.extensions.{ prf, largeBlob, credBlob }` extension-helper builders. `b.auth.passkey.conditionalAuthOptions` for `mediation: \"conditional\"` autofill. New `b.auth.fidoMds3` AAGUID metadata verifier — JWS-signed BLOB fetch + cert-chain validation against the FIDO Alliance MDS3 root, AAGUID lookup, REVOKED / USER_KEY_PHYSICAL_COMPROMISE / USER_KEY_REMOTE_COMPROMISE refusal, in-memory cache TTL = `nextUpdate - now`."
1183
+ },
1184
+ {
1185
+ "title": "`b.publicSuffix` + vendored Mozilla PSL",
1186
+ "body": "`{ publicSuffix, organizationalDomain, isPublicSuffix, lookupSource }` with vendored Mozilla PSL data (10,207 rules). Exact-match > exception > wildcard > implicit `*` per canonical algorithm; IDNA Punycode normalisation. Consumed by the DMARCbis + BIMI work."
1187
+ },
1188
+ {
1189
+ "title": "Email auth primitives",
1190
+ "body": "DMARCbis `psd=` / `np=` / org-domain discovery via PSL in `lib/mail-auth.js`. RFC 9091 BIMI VMC + CMC chain validation in `b.mail.bimi.fetchAndVerifyMark` (httpClient-fetched cert, cert-path validation against vendored BIMI Trust Anchor roots, `subjectAltName` URI match, RFC 3709 logotype extension parsing for the embedded SVG, BIMI policy OID gate). Tiny-PS SVG profile validator (`b.mail.bimi.validateTinyPsSvg`) refusing `<script>` / `<style>` / `<foreignObject>` / `<animate*>` / external refs / >32 KiB. RFC 8617 ARC trust-eval (`arcEvaluate` returns `{ trust, trustedHops, finalAr, breakAt }`). RFC 5965 ARF abuse-feedback ingestion (`b.mailArf.parse`). RFC 8601 iprev / FCrDNS verifier (`b.mail.iprev.verify` exposed via `b.mail.reverseDns`)."
1191
+ },
1192
+ {
1193
+ "title": "Email transport primitives",
1194
+ "body": "RFC 3030 BDAT / CHUNKING / BINARYMIME framing in `b.mail.send` (default chunk size 256 KiB, BODY=BINARYMIME / 8BITMIME negotiation, refuse-on-binary-without-peer-support). RFC 1870 SIZE pre-MAIL-FROM cap. IPv6 submission + AAAA preference auto-detect (`preferFamily: 4|6|\"any\"`). Generic RFC 3461/3464 DSN parser + generator in `b.mailBounce.dsn.{ parse, build }` (multipart/report message/delivery-status, RFC 6533 SMTPUTF8 EAI-aware). RFC 3798/8098 MDN builder + parser (`b.mailMdn.{ build, parse }` — refuses auto-generation when inbound message asserts `important=required`). RFC 2369 / 2919 List-Help / List-Owner / List-Archive / List-ID bundle in `b.mail.unsubscribe.buildAllListHeaders`."
1195
+ },
1196
+ {
1197
+ "title": "DNS primitives",
1198
+ "body": "RFC 9460 SVCB / HTTPS RR query + parse (`b.network.dns.{ querySvcb, queryHttps }` — AliasMode + ServiceMode, full SvcParam vocabulary including `ech` / `alpn` / `mandatory` / `dohpath` / `ipv4hint` / `ipv6hint`). RFC 7858 DoT first-class transport (`transport: \"dot\"` opt + connection pool). RFC 9462 DDR (`b.network.dns.discoverEncrypted` queries `_dns.resolver.arpa`). RFC 9463 DNR (`b.network.dns.useDesignatedResolvers` operator-side resolver advertisement). Generic `b.network.dns.resolve(name, type, opts)` dispatcher. `b.network.dns.reverse(ip)` PTR helper. RFC 9250 DoQ explicitly NOT shipped — Node QUIC is experimental; documented deferral in `lib/network-dns.js`."
1199
+ },
1200
+ {
1201
+ "title": "TLS primitives",
1202
+ "body": "ECH outbound from SVCB (`b.network.tls.connectWithEch` queries HTTPS RR, parses `ech=` SvcParam per draft-ietf-tls-esni-22 §4 ECHConfigList wire format, attaches to `tls.connect({ ech })` with feature-detect graceful degrade). RFC 9525 strict PKIX server identity verification (`b.network.tls.checkServerIdentity9525` — SAN dNSName required when present, CN-fallback refused, wildcard one-label limit, IP SAN matching)."
1203
+ },
1204
+ {
1205
+ "title": "RFC 9111 outbound HTTP cache",
1206
+ "body": "`b.httpClient.cache.create({ store, sharedCache, defaultMaxStale, revalidateInBackground })` + `b.httpClient.cache.memoryStore({ maxBytes, maxEntries, evictionPolicy })`. §3 storage decision (status allowlist + Cache-Control directives + `Vary` keying), §4.2 freshness (s-maxage > max-age > Expires-Date > heuristic 10% capped at 24h), §4.3 conditional revalidation (`If-None-Match` + `If-Modified-Since`), §5 304 merge, RFC 5861 `stale-while-revalidate` + `stale-if-error`. `Age` + `X-Blamejs-Cache: HIT|MISS|STALE|REVALIDATED` response headers; `httpclient.cache.{ hit, miss, stale, revalidated, evicted }` audit emissions."
1207
+ },
1208
+ {
1209
+ "title": "S3 SigV4 `responseHeaders` opt on presigned downloads",
1210
+ "body": "`b.objectStore.presignedDownloadUrl` accepts `{ contentDisposition?, contentType?, contentLanguage?, contentEncoding?, cacheControl?, expires? }` — adds the S3 `response-*` override query params to the signed URL so a presigned GET overrides Content-Disposition / Content-Type / etc. on the wire regardless of how the object was stored. SigV4 signing math identical (params land in canonicalQueryString before hashing). Verified end-to-end against live MinIO via `scripts/test-integration.js object-store-sigv4` (HTTP + TLS variants, server-side header honoring confirmed)."
1211
+ }
1212
+ ]
1213
+ }
1214
+ ],
1215
+ "references": [
1216
+ {
1217
+ "label": "RFC 9527 Clear-Site-Data",
1218
+ "url": "https://www.rfc-editor.org/rfc/rfc9527.html"
1219
+ },
1220
+ {
1221
+ "label": "RFC 9091 BIMI",
1222
+ "url": "https://www.rfc-editor.org/rfc/rfc9091.html"
1223
+ },
1224
+ {
1225
+ "label": "RFC 9460 SVCB / HTTPS RR",
1226
+ "url": "https://www.rfc-editor.org/rfc/rfc9460.html"
1227
+ },
1228
+ {
1229
+ "label": "RFC 9111 HTTP Caching",
1230
+ "url": "https://www.rfc-editor.org/rfc/rfc9111.html"
1231
+ },
1232
+ {
1233
+ "label": "RFC 9525 PKIX Server Identity",
1234
+ "url": "https://www.rfc-editor.org/rfc/rfc9525.html"
1235
+ }
1236
+ ]
1237
+ },
1238
+ {
1239
+ "version": "0.8.52",
1240
+ "date": "2026-05-09",
1241
+ "headline": "Cross-platform smoke flake fixes — Windows sqlite lock + macOS fs.watch budget",
1242
+ "summary": "Two pre-existing platform fragilities surfaced under v0.8.51 CI runner contention. The `lib/local-db-thin.js` corrupt-file recovery rename budget was extended for Windows, and the `watcher.test.js` event-delivery wait was extended for macOS.",
1243
+ "sections": [
1244
+ {
1245
+ "heading": "Fixed",
1246
+ "items": [
1247
+ {
1248
+ "title": "Windows sqlite-lock recovery budget too short",
1249
+ "body": "`lib/local-db-thin.js` corrupt-file recovery rename retried 5×50ms (250ms total) before giving up. Windows holds a sqlite file lock several hundred ms after `DatabaseSync.close()` returns under load, and CI runner pressure pushed the unlock past the budget. The retry window is now 20×100ms (2s total); Linux/macOS still land on the first attempt."
1250
+ },
1251
+ {
1252
+ "title": "macOS fs.watch delivery budget too short",
1253
+ "body": "`test/layer-0-primitives/watcher.test.js` waited 80ms after writing files before asserting `fs.watch` events landed. macOS delivers events later than Linux/Windows under contention. The test wait is now 300ms in all four delay sites. No primitive surface change versus v0.8.51."
1254
+ }
1255
+ ]
1256
+ }
1257
+ ]
1258
+ },
1259
+ {
1260
+ "version": "0.8.51",
1261
+ "date": "2026-05-09",
1262
+ "headline": "CI green-up follow-on for v0.8.50 — wiki workspace install + gitleaks prose retrip",
1263
+ "summary": "Two CI gates still failed on v0.8.50 and are now corrected. The wiki validator step couldn't resolve `@blamejs/core` without first installing the wiki workspace deps, and gitleaks re-flagged the v0.8.50 CHANGELOG entry because its prose quoted the same KEM-recipient destructure shape it was reporting fixed.",
1264
+ "sections": [
1265
+ {
1266
+ "heading": "Fixed",
1267
+ "items": [
1268
+ {
1269
+ "title": "Wiki validator could not resolve `@blamejs/core`",
1270
+ "body": "The workflow ran the validator without first installing the wiki workspace deps. The step now runs `npm install --silent` in `examples/wiki/` before invoking the validator. The job was also renamed from \"Wiki primitive-section convention\" to reflect the source-driven shape."
1271
+ },
1272
+ {
1273
+ "title": "Gitleaks re-flagged the v0.8.50 CHANGELOG entry",
1274
+ "body": "The prose quoted the same KEM-recipient destructure shape it was reporting fixed, retripping the generic-api-key rule on documentation text. The CHANGELOG entry was rewritten to avoid the literal token shape; `.gitleaks.toml` adds a fingerprint suppression for the historical v0.8.50 commit. No primitive surface change versus v0.8.50."
1275
+ }
1276
+ ]
1277
+ }
1278
+ ]
1279
+ },
1280
+ {
1281
+ "version": "0.8.50",
1282
+ "date": "2026-05-09",
1283
+ "headline": "CI green-up for v0.8.49 — wiki workflow step + gitleaks `@example` allowlists",
1284
+ "summary": "Two CI gates failed on the v0.8.49 push and are now corrected. The wiki validator workflow step pointed at the retired hand-authored-seeder validator, and gitleaks flagged three JSDoc `@example` blocks the source-driven migration introduced.",
1285
+ "sections": [
1286
+ {
1287
+ "heading": "Fixed",
1288
+ "items": [
1289
+ {
1290
+ "title": "Wiki validator workflow step pointed at the retired validator",
1291
+ "body": "`.github/workflows/ci.yml` still invoked `examples/wiki/test/validate-primitive-sections.js`, which the source-driven migration retired. The step now runs `validate-source-comment-blocks.js` and runs `npm install` in `examples/wiki/` first so the validator can resolve `@blamejs/core` for the opts probe."
1292
+ },
1293
+ {
1294
+ "title": "Gitleaks flagged three JSDoc `@example` blocks",
1295
+ "body": "A fake hex token in `lib/api-key.js`, a Stripe-shaped redaction example in `lib/log-stream.js`, and a KEM-recipient destructure in `lib/crypto.js` were flagged by gitleaks. The examples were rewritten to use placeholder text. `.gitleaks.toml` broadens the recipient-shape allowlist regex to cover any sibling field name, adds `test/fixtures/.*` to the path allowlist (exploit-corpus fixtures), and pins the historical commit + per-finding fingerprints. No primitive surface change versus v0.8.49."
1296
+ }
1297
+ ]
1298
+ }
1299
+ ]
1300
+ },
1301
+ {
1302
+ "version": "0.8.49",
1303
+ "date": "2026-05-09",
1304
+ "headline": "Source-driven wiki — every page generated from `@module` + `@primitive` JSDoc blocks",
1305
+ "summary": "The hand-authored seeders under `examples/wiki/seeders/prod/pages/` are retired (~40 files, ~13k lines deleted). Pages, sidebar nav, the home-page card grid, and reference pages are produced at boot from JSDoc-style comment blocks in `lib/`. Drift between code and docs is structurally impossible — the same diff that changes the function changes the documentation.",
1306
+ "sections": [
1307
+ {
1308
+ "heading": "Added",
1309
+ "items": [
1310
+ {
1311
+ "title": "`@module` + `@primitive` comment-block convention drives wiki generation",
1312
+ "body": "Every page is produced by `examples/wiki/lib/page-generator.js` walking parsed source comments. Sidebar nav, home-page card grid, and page-generator curation entries auto-derive from `@nav` / `@title` / `@card` / `@featured` / `@order` / `@slug` tags on `@module` blocks via `examples/wiki/lib/auto-site-entries.js`. Cross-cutting narrative content lives in `lib/wiki-concepts.js` `@concept` blocks. Reference pages are auto-harvested at boot from framework state by `examples/wiki/lib/harvest-{errors,env-vars,vendored-deps,cli}.js`."
1313
+ },
1314
+ {
1315
+ "title": "Comment-block validators replace the legacy seeder validator",
1316
+ "body": "`validate-source-comment-blocks.js` enforces schema, tag ordering, signature/code arity match, `@example` parses-as-JS, placeholder-pattern detectors, posture catalog, semver shape, cross-reference resolution, and metadata completeness. `validate-site-coverage.js` enforces entry/page consistency invariants. `validate-nav-coverage.js` round-trips every nav entry against the live HTTP server and asserts populated content. Discoverer `find-missing-pages.js` walks `api-snapshot.json` and surfaces namespaces still needing `@module` blocks."
1317
+ },
1318
+ {
1319
+ "title": "Backfill script for bulk metadata migration",
1320
+ "body": "`examples/wiki/scripts/backfill-module-metadata.js` populates `@nav` / `@title` / `@card` from a canonical hints table for one-shot bulk migration. ~145 lib namespaces now carry `@primitive` blocks; ~24 marked `@featured` for the home-card grid."
1321
+ }
1322
+ ]
1323
+ },
1324
+ {
1325
+ "heading": "Removed",
1326
+ "items": [
1327
+ {
1328
+ "title": "Hand-authored wiki seeders + their validator",
1329
+ "body": "`examples/wiki/seeders/prod/pages/` (~40 files, ~13k lines) deleted. The legacy `validate-primitive-sections.js` (1264 lines) and its child runner `run-example.js` (492 lines) retired."
1330
+ }
1331
+ ]
1332
+ },
1333
+ {
1334
+ "heading": "Changed",
1335
+ "items": [
1336
+ {
1337
+ "title": "`b.safeJson.canonical(value)` — unused `_opts` parameter dropped",
1338
+ "body": "Signature is now single-arg. The previous shape accepted an unused second parameter; the comment-block validator's signature/arity-match check surfaced it. No other primitive surface change."
1339
+ }
1340
+ ]
1341
+ }
1342
+ ]
1343
+ },
1344
+ {
1345
+ "version": "0.8.48",
1346
+ "date": "2026-05-09",
1347
+ "headline": "CI green-up for v0.8.47 (additional lifecycle examples commenting)",
1348
+ "summary": "Other primitive examples were creating lingering resources (intervals, scheduler ticks, sqlite handles, heartbeat timers, repeating loops) that prevented the example-execution sandbox from settling. Live invocations now commented out with `typeof` assertions kept as the executable check. No primitive surface change versus v0.8.47.",
1349
+ "sections": [
1350
+ {
1351
+ "heading": "Fixed",
1352
+ "items": [
1353
+ {
1354
+ "title": "Lifecycle-creating wiki examples commented out",
1355
+ "body": "Live invocations are now commented out (kept as documentation) for: `b.scheduler.create({...}).schedule(...)` (compliance-patterns retention sweep), `b.cluster.init({...})` + `b.cluster.requireLeader()` (cluster heartbeat timer), `b.scheduler.create({ cluster }).schedule(...)` + `await scheduler.start()` + `b.scheduler.nextCronFire(...)`, `b.localDb.thin({...})` (sqlite handle), `b.network.heartbeat.start({...})` + `b.network.heartbeat.status(...)`, `b.safeAsync.repeating(...)` + `loop.stop()`. Each section keeps a `typeof b.X.Y; // \"function\"` assertion as the executable check."
1356
+ }
1357
+ ]
1358
+ }
1359
+ ]
1360
+ },
1361
+ {
1362
+ "version": "0.8.47",
1363
+ "date": "2026-05-09",
1364
+ "headline": "CI green-up for v0.8.46 (Wiki e2e hang on the watcher example)",
1365
+ "summary": "The wiki primitive-section example for `b.watcher.create` invoked `fs.watch(root, { recursive: true })` and the recursive-watch handle never unblocked the Linux runner. The example is now commented out (kept as documentation, with a `typeof b.watcher.create` assertion as the executable check). No primitive surface change versus v0.8.46.",
1366
+ "sections": [
1367
+ {
1368
+ "heading": "Fixed",
1369
+ "items": [
1370
+ {
1371
+ "title": "Wiki e2e hang on the watcher example",
1372
+ "body": "The `b.watcher.create` example in `ops-hardening.js` was live-invoking `fs.watch(root, { recursive: true })`; the recursive-watch handle blocked the host process on the runner kernel. The live invocation is now commented out (kept as documentation) and replaced with a `typeof b.watcher.create` assertion as the executable check. Same convention reused for other lifecycle-creating examples that need operator-supplied state."
1373
+ }
1374
+ ]
1375
+ }
1376
+ ]
1377
+ },
1378
+ {
1379
+ "version": "0.8.46",
1380
+ "date": "2026-05-09",
1381
+ "headline": "CI green-up for v0.8.45 (smoke gate)",
1382
+ "summary": "Six fixes that unblock the npm-publish smoke gate: pqc-agent curve-name regex, opt-in postgres `application_name`, closure-in-loop in `connectFn`, lazyRequire shape in `subject.js`, audit namespaces for the v0.8.45 primitives, and codebase-pattern cleanups. No primitive surface change versus v0.8.45.",
1383
+ "sections": [
1384
+ {
1385
+ "heading": "Fixed",
1386
+ "items": [
1387
+ {
1388
+ "title": "`lib/pqc-agent.js` `_validateGroupName` regex",
1389
+ "body": "Relaxed to accept hyphen so operator-supplied curve names like `P-256` produce the framework-preference refusal error (the test message contract `\"not in the framework PQC-hybrid preference\"` was bypassed by an earlier illegal-character throw)."
1390
+ },
1391
+ {
1392
+ "title": "`b.externalDb.init` `applicationName` opt-in",
1393
+ "body": "Default leaves the SET unsubmitted so per-pool query-count tracking in operator tests / fakes isn't doubled by a connection-init `SET application_name TO 'blamejs'` against drivers that simply count tracker.query calls; the test at `external-db-hardening.test.js` updated to assert the opt-in behaviour."
1394
+ },
1395
+ {
1396
+ "title": "`lib/external-db.js` postgres-dialect `connectFn` closure-in-loop",
1397
+ "body": "Wrapped in an IIFE so the closure captures `rawConnect` / `rawQuery` per-iteration instead of sharing the var-hoisted bindings across the for-loop. Every backend's `connectFn` was previously calling the LAST iteration's `rawQuery`."
1398
+ },
1399
+ {
1400
+ "title": "`lib/subject.js` `legalHold._getSingleton()` shape",
1401
+ "body": "Corrected to `legalHold()._getSingleton()` — the lazyRequire helper returns a getter function, not the module."
1402
+ },
1403
+ {
1404
+ "title": "`lib/audit.js` `FRAMEWORK_NAMESPACES` extended",
1405
+ "body": "Registers every namespace emitted by the new v0.8.45 primitives (`http2`, `tenant`, `jwt`, `dr`, `guardfilename`, `legalhold`, `networkheartbeat`, `router`)."
1406
+ },
1407
+ {
1408
+ "title": "Codebase-patterns cleanup across the new lib files",
1409
+ "body": "Real-fix path (validateOpts helpers / safeBuffer.isHex / safeEnv.readVar / safeBuffer.boundedChunkCollector / shared regex constant) plus per-cluster KNOWN_CLUSTERS allowlist entries with structural reasons keyed on the reporter's stable cluster fingerprint."
1410
+ }
1411
+ ]
1412
+ }
1413
+ ]
1414
+ },
1415
+ {
1416
+ "version": "0.8.45",
1417
+ "date": "2026-05-09",
1418
+ "headline": "Wiki rebuild + 14 primitives + 9 enhancements + v0.8.44 CI recovery",
1419
+ "summary": "v0.8.44's npm-publish workflow failed at the framework smoke gate; v0.8.45 ships the cumulative recovery on top of the v0.8.44 commit. Three CI fixes from v0.8.44, fourteen new primitives (httpClient stream helpers, OS keychain, worker pool, watcher, thin localDb, daemon glue, self-update, hash helpers, bounded mapAsync, TLS option builder, passive heartbeat, arg parser, byte quota), nine surface enhancements, and a Scorecard workflow split.",
1420
+ "sections": [
1421
+ {
1422
+ "heading": "Added",
1423
+ "items": [
1424
+ {
1425
+ "title": "Streaming HTTP helpers",
1426
+ "body": "`b.httpClient.downloadStream({ url, dest, hash, expected })` streams to `<dest>.tmp`, hashes while piping, atomic-renames on hash match, refuses + deletes on mismatch (returns `{ statusCode, bytesWritten, hash }`). `b.httpClient.uploadMultipartStream({ url, fields, file })` streams a file body into multipart POST without buffering."
1427
+ },
1428
+ {
1429
+ "title": "`b.keychain.{ store, retrieve, remove }`",
1430
+ "body": "OS keychain abstraction across macOS `security`, Linux `secret-tool`, Windows PowerShell, plus a 0o600 XChaCha20-Poly1305-sealed file fallback. Native paths use stdin (process-list-safe)."
1431
+ },
1432
+ {
1433
+ "title": "`b.workerPool.create(scriptPath, opts)`",
1434
+ "body": "`node:worker_threads` pool with bounded concurrency, per-task timeout, worker recycle on uncaught error, and `{ run, drain, terminate, stats }`."
1435
+ },
1436
+ {
1437
+ "title": "`b.watcher.create`",
1438
+ "body": "Recursive `fs.watch` wrapper with per-path debounce, glob-style ignore, symlink-skip, and cross-platform event normalisation."
1439
+ },
1440
+ {
1441
+ "title": "`b.localDb.thin({ file, schemaSql, recovery })`",
1442
+ "body": "Lightweight `node:sqlite` wrapper with WAL + integrity check + corrupt-rename-and-recreate recovery + prepared-statement cache. No vault encryption / audit chain — for desktop daemon-style state where `b.db` is too heavy."
1443
+ },
1444
+ {
1445
+ "title": "`b.daemon.{ start, stop }`",
1446
+ "body": "PID file write + stale-PID reap + signal handling glue around `b.appShutdown` + cross-platform detached fork via `b.processSpawn`."
1447
+ },
1448
+ {
1449
+ "title": "`b.selfUpdate.{ poll, verify, swap, rollback }`",
1450
+ "body": "GitHub-releases poll + ETag/304 + signed-asset verify (composes `b.crypto.verify` for ML-DSA-87 / Ed25519 / EC) + atomic swap with EXDEV cross-device fallback + rollback on health failure."
1451
+ },
1452
+ {
1453
+ "title": "`b.crypto.hashFile` + `b.crypto.hashStream`",
1454
+ "body": "Streaming hash from disk / arbitrary readable (default sha3-512). Uses `node:stream/promises` pipeline."
1455
+ },
1456
+ {
1457
+ "title": "`b.safeAsync.parallel(items, fn, { concurrency })`",
1458
+ "body": "Bounded-concurrency mapAsync with continuous worker queue (no Promise.all-batched chunks). Default 8, max 256, AbortSignal support."
1459
+ },
1460
+ {
1461
+ "title": "`b.network.tls.buildOptions`",
1462
+ "body": "TLS request-options builder that knows the framework's PQC group preference + TLS 1.3 floor; centralizes posture for operators that build their own `https.Agent`."
1463
+ },
1464
+ {
1465
+ "title": "`b.network.heartbeat.passive`",
1466
+ "body": "Server-pushed heartbeat consumer (inverse of the existing active probe). Caller invokes `recordPong()` on heartbeat frames; `onTimeout()` fires once if `timeoutMs` elapses without a pong."
1467
+ },
1468
+ {
1469
+ "title": "`b.argParser.create`",
1470
+ "body": "Reusable CLI arg parser extracted from `lib/cli.js`. Type coercion (string / number / boolean / list), per-command flag scoping, `--` terminator, prototype-pollution defense, `parseRaw(argv)` low-level alias. `lib/cli.js` refactored to compose the new primitive."
1471
+ },
1472
+ {
1473
+ "title": "`b.network.byteQuota.create`",
1474
+ "body": "Extracted from `b.middleware.dailyByteQuota`; exposes `{ check(key, bytes), record(key, bytes), reset(key), snapshot() }` for preflight checks before accept (when file size is known at headers-parsed time)."
1475
+ }
1476
+ ]
1477
+ },
1478
+ {
1479
+ "heading": "Changed",
1480
+ "items": [
1481
+ {
1482
+ "title": "`b.pqcAgent` accepts `SecP256r1MLKEM768`",
1483
+ "body": "RFC 9794 codepoint shipped in v0.8.44 — now reachable. Adds `allowOperatorGroups: false` opt; when `true`, `create()` accepts any IANA-known TLS group with audit emit `pqcagent.operator_group.accepted`. `lib/constants.js` `TLS_GROUP_PREFERENCE` now `[X25519MLKEM768, SecP384r1MLKEM1024, SecP256r1MLKEM768]`."
1484
+ },
1485
+ {
1486
+ "title": "`b.archive.zip().toStream(writable)` streaming archives",
1487
+ "body": "Pipes the assembled archive directly to an operator-supplied `Writable` (or returns a `Readable` if no writable supplied). `addFile(name, Readable)` accepts streamed sources; APPNOTE 4.4.4 bit-3 data-descriptors. Atomic finalize: central directory written only after every entry resolves; on source error destination is destroyed with `archive/aborted` and EOCD never emitted. `toBuffer()` refuses streaming entries with `archive/streaming-entry`."
1488
+ },
1489
+ {
1490
+ "title": "`b.guardFilename.sanitize({ mode: \"strip\" })`",
1491
+ "body": "Replaces CR / LF / HT / VT / FF / C0 controls / bidi-override / zero-width with `_` for `Content-Disposition` use. Path-traversal / null-byte / NTFS ADS / UNC / overlong UTF-8 floor still throws at every profile level."
1492
+ },
1493
+ {
1494
+ "title": "`b.middleware.rateLimit` algorithm opt-in",
1495
+ "body": "`{ algorithm: \"fixed-window\" | \"token-bucket\" }` — fixed-window opts in alongside the existing token-bucket default."
1496
+ },
1497
+ {
1498
+ "title": "`b.parsers.json` + `b.parsers.multipart` standalone helpers",
1499
+ "body": "Standalone async helpers for handlers that lazy-parse without mounting bodyParser as middleware."
1500
+ },
1501
+ {
1502
+ "title": "`b.router.create({ allowedRedirectOrigins })`",
1503
+ "body": "Exact-match HTTPS-origin allowlist for `res.redirect` cross-origin (OAuth bounces). Off-allowlist redirects throw `RouterError(\"router/redirect-cross-origin-refused\", ...)`."
1504
+ },
1505
+ {
1506
+ "title": "`b.requestHelpers.extractBearer(req)`",
1507
+ "body": "Inbound RFC 6750 bearer extractor; case-insensitive `Bearer` prefix, refuses multiple `Authorization` headers (CWE-345), refuses control / CR / LF / NUL bytes; returns null on missing/malformed."
1508
+ },
1509
+ {
1510
+ "title": "`b.crypto.namespaceHash(prefix, value)`",
1511
+ "body": "Hex SHA3-512 of `prefix + \":\" + value` for indexable derived-hash columns. Refuses NUL / CR / LF / oversized prefix."
1512
+ },
1513
+ {
1514
+ "title": "Scorecard workflow split",
1515
+ "body": "`.github/workflows/scorecard.yml` split into a `uses:`-only `analysis` job and a separate `threshold` job that downloads the SARIF artifact; analysis no longer fails the scorecard-action workflow restriction. `SCORECARD_MIN` default lowered to 6.0 (structural floor for a < 90-day single-maintainer pre-1.0 repo)."
1516
+ }
1517
+ ]
1518
+ },
1519
+ {
1520
+ "heading": "Fixed",
1521
+ "items": [
1522
+ {
1523
+ "title": "v0.8.44 carry-over CI fixes",
1524
+ "body": "`lib/backup/bundle.js` manifest-signing is now best-effort when `auditSign.init()` has not been awaited (bundle ships unsigned with a `manifest-unsigned` progress event; `restoreBundle.extract({ requireSignature: true })` continues to refuse unsigned manifests). `lib/external-db.js` `SET application_name` is now best-effort so non-Postgres test fakes shimming the postgres dialect don't fail at connection init. `b.subject.erase` accepts an explicit `opts.legalHold` instance to avoid the process-global singleton race in parallel-test runs."
1525
+ }
1526
+ ]
1527
+ }
1528
+ ]
1529
+ },
1530
+ {
1531
+ "version": "0.8.44",
1532
+ "date": "2026-05-08",
1533
+ "headline": "Compliance / crypto / transport additive surface + CVE sweep + wiki restructure",
1534
+ "summary": "Adds a substantial compliance-primitive surface (21 CFR Part 11, PCI 10.4 daily review, SOX / SOC 2 SoD triggers, m-of-n DDL change control, legal-hold, WORM, dual-control + crypto-erase, per-row K_row tagging, tenant quota, DR runbook, backup-test scheduler, CADF audit envelope, JSON-Schema 2020-12 metadata, Debezium outbox, safeJsonPath, role-hardening, outbound DLP, bot challenge, device binding, sandbox, FAPI 2 / FDX / TCPA 10DLC / IAB MSPA postures). Standards-track additions: HPKE, TLS-Exporter, HTTP Message Signatures, CT v2 inclusion proofs, ACME + ARI, 0-RTT inbound posture, PQ TLS group preference. ML-DSA-65 opt-in. Closes 10+ CVE-classes. Pins Node engine to `>=24.14.1`.",
1535
+ "sections": [
1536
+ {
1537
+ "heading": "Added",
1538
+ "items": [
1539
+ {
1540
+ "title": "Compliance / regulatory primitives",
1541
+ "body": "`b.fda21cfr11` (21 CFR Part 11 §11.10(e) audit-content + §11.50(b) e-signature shape); `b.auditDailyReview` (PCI DSS 4.0 Req 10.4.1.1 daily-review automation through `b.scheduler`); `b.audit.bindActor` + `b.audit.assertSegregation` (SOX §404 + SOC 2 CC1.3 segregation-of-duties Postgres triggers); `b.ddlChangeControl` (m-of-n approver DDL change-control with maintenance windows and ML-DSA-87 signed proposals); `b.legalHold` (FRCP Rule 26/37(e), GDPR Art 17(3)(e), SEC Rule 17a-4, HIPAA §164.530(j)(2)); `b.db.declareWorm`; `b.db.declareRequireDualControl` + `b.db.eraseHard` (dual-control delete + crypto-erase + REINDEX); `b.cryptoField.declareColumnResidency` / `declarePerRowKey` + `b.subject.eraseHard`; `b.tenantQuota`; `b.drRunbook.emit`; `b.backup.scheduleTest` + `b.backupBundle.verifyManifestSignature`; `b.db.exportCsv` (RFC 4180 strict + SHA3-512 + optional ML-DSA-87); `b.audit.export({ format: \"cadf\" })` (ISO/IEC 19395 CADF envelope); `b.db.getTableMetadata({ format: \"json-schema-2020-12\" })`; `b.outbox.create({ envelope: \"debezium\" })`; `b.safeJsonPath` (refuses filter / deep-scan / script-shape / control-char input; wired into `b.db.where` for JSONB ops); `b.externalDb.assertRoleHardening`; `b.redact.installOutboundDlp` (httpClient + mail + webhook DLP with PAN / SSN / EIN / IBAN / api-key / PEM / SSH / JWT / AWS detectors); `b.authBotChallenge`; `b.sessionDeviceBinding` (SHAKE256 fingerprint over UA + Accept-Language + Accept-Encoding + IP /24 (or /48) prefix + optional cryptographic boundKey); `b.sandbox`; `b.fapi2`, `b.fdx`, `b.tcpa10dlc`, `b.iabMspa` posture cascade entries."
1542
+ },
1543
+ {
1544
+ "title": "Standards-track crypto + transport",
1545
+ "body": "`b.crypto.hpke` (RFC 9180 with ML-KEM-1024 + HKDF-SHA3-512 + ChaCha20-Poly1305); `b.tlsExporter` (RFC 9266 channel binding); `b.crypto.httpSig` (RFC 9421 HTTP Message Signatures, ed25519 + ML-DSA-65); `b.network.tls.ct.verifyInclusion` (RFC 9162 CT v2 inclusion proofs); `b.acme` (RFC 8555 + RFC 9773 ARI for the CA/B Forum 47-day cert lifetime); `b.router.create({ tls0Rtt })` (RFC 8470 0-RTT inbound posture, default `refuse`); `b.network.tls.preferredGroups.{set,get,reset}` (default `X25519MLKEM768`, `SecP256r1MLKEM768`, `SecP384r1MLKEM1024`, `X25519`)."
1546
+ },
1547
+ {
1548
+ "title": "ML-DSA-65 opt-in on `b.audit-sign` and `b.webhook`",
1549
+ "body": "`lib/audit-sign.js` and `lib/webhook.js` accept `algorithm: \"ML-DSA-65\"` for smaller signatures + faster verify; default remains SLH-DSA-SHAKE-256f."
1550
+ }
1551
+ ]
1552
+ },
1553
+ {
1554
+ "heading": "Fixed",
1555
+ "items": [
1556
+ {
1557
+ "title": "CVE sweep",
1558
+ "body": "CVE-2026-21712 (codebase-patterns refuses `url.format(`). CVE-2026-21714 (h2 GOAWAY tracking refuses post-GOAWAY stream activity). CVE-2026-21717 (`b.safeJson` `maxKeys` cap, default 10000, ABSOLUTE_MAX 1000000). CVE-2026-33870 (body-parser differentiates `HPE_CHUNK_EXTENSIONS_OVERFLOW` and emits `http.chunked.extension.refused`). CVE-2026-29000 / 23993 / 22817 / 34950 (auth-jwt-external refuses 5-segment JWE tokens with `auth-jwt-external/jwe-refused` audit). CVE-2026-25639 / 42033 / 42041 / 40175 (vendor-deny gate refuses `axios`, `xml-crypto`, `samlify`, `xml2js`). SECURITY.md gains entries for CVE-2026-21715 / 21716 (Permission Model + symlink defenses), CVE-2026-23918 / CVE-2026-33555 (Apache + HAProxy reverse-proxy floors), CVE-2026-26996 / 33671 / 27904 (pubsub `_MAX_CHANNEL_LEN`), CVE-2026-25922 / 23687 / 34840 (SAML signed-bytes invariant), CVE-2026-34511 (oauth audited clean)."
1559
+ },
1560
+ {
1561
+ "title": "Node engine floor lifted to `>=24.14.1`",
1562
+ "body": "CVE-2026-21713 non-constant-time HMAC compare."
1563
+ }
1564
+ ]
1565
+ },
1566
+ {
1567
+ "heading": "Changed",
1568
+ "items": [
1569
+ {
1570
+ "title": "Wiki restructure: 25 `guard-*` pages consolidated into 7 grouped pages",
1571
+ "body": "`guard-overview`, `guard-content`, `guard-structured-data`, `guard-identifiers`, `guard-protocols`, `guard-execution`, `guard-binary`, `guard-aggregate`. New pages `ai-governance`, `api-contracts`, `ops-hardening`, `regulatory-reporting`. Wiki seeder text corrected to describe Argon2id routing through Node 24+'s built-in `crypto.argon2*` API (no vendored native module)."
1572
+ },
1573
+ {
1574
+ "title": "New compliance postures cascade through `POSTURE_DEFAULTS`",
1575
+ "body": "`sox-404`, `soc2-cc1.3`, `fda-21cfr11`, `fda-annex-11`, `sec-17a-4`, `finra-4511`, `dpdp`, `pipl-cn`, `lgpd-br`, `appi-jp`, `pdpa-sg`, `staterramp`, `irap`, `bsi-c5`, `ens-es`, `uk-g-cloud`. Cascade through retention / audit / db / cryptoField."
1576
+ },
1577
+ {
1578
+ "title": "Workflow-level `permissions: contents: read` + SHA-pinned 40 actions",
1579
+ "body": "`ci.yml` / `npm-publish.yml` / `release-container.yml` declare least-privilege at workflow level; per-job blocks elevate only where needed. Forty GitHub Actions are SHA-pinned (zero `@master` / floating refs). Scorecard threshold gate reads aggregate score from `results.sarif` and fails when below `vars.SCORECARD_MIN` (default 7.0; fails-open with warning when SARIF shape changes). Dependabot extended to root npm `devDependencies`. `npm-publish.yml` build-summary refactored from `${{ steps.* }}` direct interpolation to `env:` block + `${VAR}` substitution."
1580
+ }
1581
+ ]
1582
+ }
1583
+ ],
1584
+ "references": [
1585
+ {
1586
+ "label": "RFC 9180 HPKE",
1587
+ "url": "https://www.rfc-editor.org/rfc/rfc9180.html"
1588
+ },
1589
+ {
1590
+ "label": "RFC 9421 HTTP Message Signatures",
1591
+ "url": "https://www.rfc-editor.org/rfc/rfc9421.html"
1592
+ },
1593
+ {
1594
+ "label": "RFC 9162 Certificate Transparency v2",
1595
+ "url": "https://www.rfc-editor.org/rfc/rfc9162.html"
1596
+ },
1597
+ {
1598
+ "label": "RFC 8555 ACME",
1599
+ "url": "https://www.rfc-editor.org/rfc/rfc8555.html"
1600
+ },
1601
+ {
1602
+ "label": "RFC 9773 ACME ARI",
1603
+ "url": "https://www.rfc-editor.org/rfc/rfc9773.html"
1604
+ }
1605
+ ]
1606
+ },
1607
+ {
1608
+ "version": "0.8.43",
1609
+ "date": "2026-05-07",
1610
+ "headline": "Explicit `USER 65532:65532` in `examples/wiki/Dockerfile` runtime stage",
1611
+ "summary": "Chainguard's `cgr.dev/chainguard/node:latest` already runs as `nonroot` (UID 65532) by default, but Trivy's static Dockerfile checker (DS-0002) flags any image without a literal `USER` line regardless of base-image default. Behavior unchanged.",
1612
+ "sections": [
1613
+ {
1614
+ "heading": "Changed",
1615
+ "items": [
1616
+ {
1617
+ "title": "`examples/wiki/Dockerfile` runtime stage declares `USER 65532:65532`",
1618
+ "body": "Trivy DS-0002 was flagging the image despite the base-image default running as nonroot. Adding the literal directive clears the static-Dockerfile check."
1619
+ }
1620
+ ]
1621
+ }
1622
+ ]
1623
+ },
1624
+ {
1625
+ "version": "0.8.42",
1626
+ "date": "2026-05-07",
1627
+ "headline": "DB hardening + vault-PEM hardening + OWASP secrets-in-env hygiene + statement-cache + vacuum-after-erase",
1628
+ "summary": "Per-deployment salt for derived hashes (HIPAA Safe Harbor), sealed break-glass kwGrantHalf, Postgres transaction timeouts with deadlock retries, SQLite tmpfs path advisory, prepared-statement LRU, post-erase VACUUM helper, coarse-bucketed `__erasedAt`, ISO-8601 surfacing on audit reads, env-strip on child spawn, and three vault.sealPemFile sub-fixes.",
1629
+ "sections": [
1630
+ {
1631
+ "heading": "Added",
1632
+ "items": [
1633
+ {
1634
+ "title": "`b.db.vacuumAfterErase({ mode, pages })`",
1635
+ "body": "Runs `VACUUM` / `PRAGMA incremental_vacuum` after large erasures (right-to-be-forgotten support)."
1636
+ },
1637
+ {
1638
+ "title": "`b.processSpawn.spawn(command, args, { allowEnv })`",
1639
+ "body": "Strips `DATABASE_URL` / `PG*` / `AWS_*` / `*_API_KEY` / `*_SECRET` / `*_TOKEN` etc. from the child env by default (OWASP secrets-in-env hygiene)."
1640
+ },
1641
+ {
1642
+ "title": "`b.auditTools.withRecordedAtIso(row)`",
1643
+ "body": "Surfaces ISO-8601 alongside Unix-ms without disturbing the chain-hash canonical form."
1644
+ }
1645
+ ]
1646
+ },
1647
+ {
1648
+ "heading": "Changed",
1649
+ "items": [
1650
+ {
1651
+ "title": "Per-deployment derived-hash salt",
1652
+ "body": "`b.cryptoField.derivedHashes` binds a per-deployment 32-byte salt (persisted at `<dataDir>/vault.derived-hash-salt`) so the same plaintext produces different hashes across deployments — defends against rainbow-table reuse and supports HIPAA Safe Harbor §164.514(b)(2)(i)."
1653
+ },
1654
+ {
1655
+ "title": "Sealed break-glass `kwGrantHalf`",
1656
+ "body": "`_blamejs_break_glass_grants.kwGrantHalf` is now sealed under the vault key."
1657
+ },
1658
+ {
1659
+ "title": "Postgres transaction timeouts + deadlock retries",
1660
+ "body": "`b.externalDb.transaction({ statementTimeoutMs, idleInTransactionTimeoutMs, deadlockRetries })` enforces SET-LOCAL Postgres timeouts and auto-retries 40P01 / 40001 with jittered backoff."
1661
+ },
1662
+ {
1663
+ "title": "SQLite tmpfs path advisory",
1664
+ "body": "Boot-time warning when the SQLite tmpfs path doesn't resolve under `/dev/shm` / `/run/shm` / `/run/user` / `/tmp`."
1665
+ },
1666
+ {
1667
+ "title": "`b.db.prepare` Statement-handle LRU cache",
1668
+ "body": "Caches Statement handles (LRU 256, cleared on init/close) so long-running daemons don't leak fds."
1669
+ },
1670
+ {
1671
+ "title": "`__erasedAt` coarse-bucketed to 1-day floor",
1672
+ "body": "Removes the sub-day forensic timing fingerprint on right-to-be-forgotten rows."
1673
+ },
1674
+ {
1675
+ "title": "`b.vault.sealPemFile` parent-dir + fsync + watch cadence",
1676
+ "body": "Asserts parent-dir mode 0o755 or stricter, fsyncs the destination directory after rename, and reduces `fs.watchFile` cadence from 2s to 500ms."
1677
+ }
1678
+ ]
1679
+ }
1680
+ ]
1681
+ },
1682
+ {
1683
+ "version": "0.8.41",
1684
+ "date": "2026-05-07",
1685
+ "headline": "Breaking envelope wire-format bump (0xE2 magic) + new audit, password, KAT, lock, config, backup, KEM primitives",
1686
+ "summary": "`b.crypto.encrypt` now produces 0xE2-magic envelopes that bind NIST SP 800-56C r2 / RFC 9180 FixedInfo (kemId/cipherId/kdfId + `blamejs/v1` label) into the SHAKE256 KDF input AND the 4-byte envelope header into the XChaCha20-Poly1305 AAD; legacy 0xE1 envelopes are refused. Operators with framework-sealed data must regenerate it. Ships `b.canonicalJson.stringifyJcs`, `b.auth.password.gate(n)`, `b.pqcSoftware.runKnownAnswerTest`, `b.resourceAccessLock`, `b.config.loadDbBacked`, `b.backup.runInWorker`, `b.config.create.reload/subscribe`. Tightens ARC / A-R / MTA-STS / CT spec-conformance. Detects release-named test files at gate time.",
1687
+ "sections": [
1688
+ {
1689
+ "heading": "Added",
1690
+ "items": [
1691
+ {
1692
+ "title": "`b.canonicalJson.stringifyJcs`",
1693
+ "body": "RFC 8785 JSON Canonicalization Scheme strict mode."
1694
+ },
1695
+ {
1696
+ "title": "`b.auth.password.gate(n)`",
1697
+ "body": "Process-global Argon2id concurrency semaphore."
1698
+ },
1699
+ {
1700
+ "title": "`b.pqcSoftware.runKnownAnswerTest`",
1701
+ "body": "Boot-time KAT for the vendored PQC primitives."
1702
+ },
1703
+ {
1704
+ "title": "`b.resourceAccessLock`",
1705
+ "body": "Three-mode lock for non-HTTP resources (counterpart to `b.auth.accessLock`)."
1706
+ },
1707
+ {
1708
+ "title": "`b.config.loadDbBacked`",
1709
+ "body": "DB-row-backed hot-reload config baseline + overlay."
1710
+ },
1711
+ {
1712
+ "title": "`b.backup.runInWorker`",
1713
+ "body": "Worker_threads dispatch for the backup pipeline."
1714
+ },
1715
+ {
1716
+ "title": "`b.config.create({...}).reload/subscribe`",
1717
+ "body": "Reactive reload + subscription on the config handle."
1718
+ }
1719
+ ]
1720
+ },
1721
+ {
1722
+ "heading": "Changed",
1723
+ "items": [
1724
+ {
1725
+ "title": "BREAKING: `b.crypto.encrypt` envelope wire-format bump to 0xE2 magic",
1726
+ "body": "Binds NIST SP 800-56C r2 / RFC 9180 FixedInfo (kemId/cipherId/kdfId + `blamejs/v1` label) into the SHAKE256 KDF input AND the 4-byte envelope header into the XChaCha20-Poly1305 AAD. Legacy 0xE1 envelopes are refused — operators with framework-sealed data must regenerate it."
1727
+ },
1728
+ {
1729
+ "title": "Spec-conformance tightenings",
1730
+ "body": "ARC hop-instance regex bounded per RFC 8617 §4.2.1. Authentication-Results pvalue ABNF tightened per RFC 8601 §2.3. MTA-STS HTTPS cert validation against `mta-sts.<domain>` per RFC 8461 §3.3. CT `verifyScts` algorithm-OID scope cross-checked against the log key per RFC 6962 §2.1.4."
1731
+ }
1732
+ ]
1733
+ },
1734
+ {
1735
+ "heading": "Detectors",
1736
+ "items": [
1737
+ {
1738
+ "title": "Release-named test-file detector",
1739
+ "body": "New `codebase-patterns.test.js` + `smoke.js` entry refuses release-bucket and slot-bucket test filenames."
1740
+ }
1741
+ ]
1742
+ }
1743
+ ],
1744
+ "references": [
1745
+ {
1746
+ "label": "NIST SP 800-56C r2",
1747
+ "url": "https://csrc.nist.gov/pubs/sp/800/56/c/r2/final"
1748
+ },
1749
+ {
1750
+ "label": "RFC 9180 HPKE",
1751
+ "url": "https://www.rfc-editor.org/rfc/rfc9180.html"
1752
+ },
1753
+ {
1754
+ "label": "RFC 8785 JCS",
1755
+ "url": "https://www.rfc-editor.org/rfc/rfc8785.html"
1756
+ }
1757
+ ]
1758
+ },
1759
+ {
1760
+ "version": "0.8.40",
1761
+ "date": "2026-05-07",
1762
+ "headline": "`b.honeytoken` + `b.middleware.cspReport` + `b.auditTools.forensicSnapshot` + `b.network.tls.pinsetDriftMonitor` + Scorecard CI",
1763
+ "summary": "Adds four operator-facing primitives plus the OpenSSF Scorecard workflow. Defers operator-supplied transform sandbox, chaos drills, and exploit replay corpus with re-open conditions (operator demand or CVE replay needing a vendored harness).",
1764
+ "sections": [
1765
+ {
1766
+ "heading": "Added",
1767
+ "items": [
1768
+ {
1769
+ "title": "`b.honeytoken.create({ audit })`",
1770
+ "body": "Issues canary api-key / session / URL / row-id values that emit `honeytoken.tripped` audit on any positive lookup."
1771
+ },
1772
+ {
1773
+ "title": "`b.middleware.cspReport.create({ onReport })`",
1774
+ "body": "Reporting-API endpoint that ingests CSP / COEP / COOP violations as `csp.violation` audit rows."
1775
+ },
1776
+ {
1777
+ "title": "`b.auditTools.forensicSnapshot({ out, since, passphrase, reason })`",
1778
+ "body": "Composes an audit-export slice + IR context manifest into one tamper-evident bundle for legal / regulator handover."
1779
+ },
1780
+ {
1781
+ "title": "`b.network.tls.pinsetDriftMonitor({ intervalMs })`",
1782
+ "body": "Periodically compares the trust-store fingerprint set to the captured baseline and emits `network.tls.pinset.drifted` when CAs are added or removed."
1783
+ },
1784
+ {
1785
+ "title": "OpenSSF Scorecard CI workflow",
1786
+ "body": "Adds `.github/workflows/scorecard.yml` for continuous repo-posture scoring."
1787
+ }
1788
+ ]
1789
+ }
1790
+ ]
1791
+ },
1792
+ {
1793
+ "version": "0.8.39",
1794
+ "date": "2026-05-07",
1795
+ "headline": "`b.configDrift.verifyVendorIntegrity` + `b.network.allowlist` + `b.auth.atoKillSwitch`",
1796
+ "summary": "Three operator enhancements: boot-time vendored-bundle integrity re-hash, a per-call outbound URL gate over `b.ssrfGuard`, and a composite ATO incident-response workflow that destroys sessions + applies lockout + flips access-lock mode in one audited call.",
1797
+ "sections": [
1798
+ {
1799
+ "heading": "Added",
1800
+ "items": [
1801
+ {
1802
+ "title": "`b.configDrift.verifyVendorIntegrity()`",
1803
+ "body": "Re-hashes every file listed in `lib/vendor/MANIFEST.json` at boot and refuses on mismatch."
1804
+ },
1805
+ {
1806
+ "title": "`b.network.allowlist.create({ allow, deny })`",
1807
+ "body": "Composes on `b.ssrfGuard` to gate per-call outbound URLs against an operator CIDR / host allow set."
1808
+ },
1809
+ {
1810
+ "title": "`b.auth.atoKillSwitch.trigger({ userId, reason })`",
1811
+ "body": "Composite ATO incident-response workflow that destroys every session for the user, applies `b.auth.lockout`, and optionally flips `b.auth.accessLock` mode in one audited call."
1812
+ }
1813
+ ]
1814
+ }
1815
+ ]
1816
+ },
1817
+ {
1818
+ "version": "0.8.38",
1819
+ "date": "2026-05-07",
1820
+ "headline": "Multipart parser refuses obsolete line folding + adds RFC 5987 / 8187 extended-parameter support",
1821
+ "summary": "Refuses obsolete line folding per RFC 9112 §5.2 and CR / LF / NUL bytes in part-header values per RFC 9110 §5.5. Adds RFC 5987 / 8187 `filename*=UTF-8''…` extended-parameter support — the decoded value takes precedence over a legacy `filename=` companion.",
1822
+ "sections": [
1823
+ {
1824
+ "heading": "Changed",
1825
+ "items": [
1826
+ {
1827
+ "title": "Multipart parser refuses `obs-fold` + CR / LF / NUL in part-header values",
1828
+ "body": "RFC 9112 §5.2 obsolete line folding is now refused. CR / LF / NUL bytes in part-header values are refused per RFC 9110 §5.5."
1829
+ }
1830
+ ]
1831
+ },
1832
+ {
1833
+ "heading": "Added",
1834
+ "items": [
1835
+ {
1836
+ "title": "RFC 5987 / 8187 extended-parameter filename support",
1837
+ "body": "Adds `filename*=UTF-8''…` extended-parameter decoding to multipart. The decoded value takes precedence over a legacy `filename=` companion."
1838
+ }
1839
+ ]
1840
+ }
1841
+ ]
1842
+ },
1843
+ {
1844
+ "version": "0.8.37",
1845
+ "date": "2026-05-08",
1846
+ "headline": "Audit-purge anchor CHECK constraint + `personalDataCategories` vocabulary validation",
1847
+ "summary": "Closes a silent typo class on the audit-purge anchor scope and validates operator-supplied personal-data categories against the GDPR Article 9 special-category vocabulary plus framework general categories.",
1848
+ "sections": [
1849
+ {
1850
+ "heading": "Added",
1851
+ "items": [
1852
+ {
1853
+ "title": "`_blamejs_audit_purge_anchor.scope` CHECK constraint",
1854
+ "body": "Constrains `scope IN ('audit', 'consent')`. Pre-v0.8.37 a typo silently created a parallel anchor; the chain verifier walked the wrong anchor and missed tampering."
1855
+ },
1856
+ {
1857
+ "title": "`personalDataCategories` vocabulary validation at `db.init`",
1858
+ "body": "Operator-supplied categories validated against the GDPR Article 9 special-category vocabulary + the framework's general categories. Unknown categories don't refuse (operators have legitimate custom labels) but emit a `db.personal_data_category_unknown` audit row so typos surface in regulator reviews. Allowed: `name`, `email`, `phone`, `address`, `ip`, `id-document`, `biometric`, `health`, `genetic`, `sexual-orientation`, `racial-or-ethnic-origin`, `political-opinion`, `religious-belief`, `trade-union-membership`, `criminal-record`, `financial`, `location`, `behavioral`, `device-id`, `child-data`, `education`, `employment`, `operator-defined`."
1859
+ }
1860
+ ]
1861
+ }
1862
+ ]
1863
+ },
1864
+ {
1865
+ "version": "0.8.36",
1866
+ "date": "2026-05-08",
1867
+ "headline": "HTTP / web cleanup — WebSocket handshake, Permissions-Policy, scope-aware bearer challenge",
1868
+ "summary": "Refinements against the WebSocket handshake key validator, the Permissions-Policy default for `fullscreen`, the bearer-auth `insufficient_scope` challenge surface, list-header token grammar, and multipart boundary validation.",
1869
+ "sections": [
1870
+ {
1871
+ "heading": "Added",
1872
+ "items": [
1873
+ {
1874
+ "title": "`b.middleware.bearerAuth` insufficient_scope (RFC 6750 §3)",
1875
+ "body": "New `requiredScopes: [\"scope1\", \"scope2\"]` opt enforces operator-declared scopes. The token's `user.scope` (space-separated string) or `user.scopes` (array) is checked; missing scopes refuse with HTTP 403 + `WWW-Authenticate: Bearer error=\"insufficient_scope\", scope=\"...\"`."
1876
+ }
1877
+ ]
1878
+ },
1879
+ {
1880
+ "heading": "Changed",
1881
+ "items": [
1882
+ {
1883
+ "title": "`b.requestHelpers.parseListHeader({ strictToken: true })`",
1884
+ "body": "RFC 9110 §5.6.2 token-grammar enforcement. Refuses non-token entries (anything outside `!#$%&'*+-.^_`|~` plus alphanumerics)."
1885
+ },
1886
+ {
1887
+ "title": "Permissions-Policy `fullscreen` flipped to deny by default",
1888
+ "body": "`b.middleware.securityHeaders` now emits `fullscreen=()` (deny) instead of `fullscreen=(self)`. Operators wanting fullscreen pass an explicit override."
1889
+ }
1890
+ ]
1891
+ },
1892
+ {
1893
+ "heading": "Fixed",
1894
+ "items": [
1895
+ {
1896
+ "title": "WebSocket handshake `Sec-WebSocket-Key` validated (RFC 6455 §4.1)",
1897
+ "body": "The handshake key is now validated as base64 of 16 random bytes (`/^[A-Za-z0-9+/]{22}==$/`). Previously only presence was checked; truncated and arbitrary-token values flowed through."
1898
+ },
1899
+ {
1900
+ "title": "Multipart boundary validation (RFC 2046 §5.1.1)",
1901
+ "body": "`_parseMultipart` refuses boundaries longer than 70 chars or violating the `bcharsnospace` grammar. Closes the quadratic-match risk on pathological boundaries."
1902
+ }
1903
+ ]
1904
+ }
1905
+ ],
1906
+ "references": [
1907
+ {
1908
+ "label": "RFC 6750 OAuth 2.0 Bearer Token Usage",
1909
+ "url": "https://www.rfc-editor.org/rfc/rfc6750.html"
1910
+ },
1911
+ {
1912
+ "label": "RFC 6455 WebSocket",
1913
+ "url": "https://www.rfc-editor.org/rfc/rfc6455.html"
1914
+ },
1915
+ {
1916
+ "label": "RFC 9110 HTTP Semantics",
1917
+ "url": "https://www.rfc-editor.org/rfc/rfc9110.html"
1918
+ },
1919
+ {
1920
+ "label": "RFC 2046 MIME Media Types",
1921
+ "url": "https://www.rfc-editor.org/rfc/rfc2046.html"
1922
+ }
1923
+ ]
1924
+ },
1925
+ {
1926
+ "version": "0.8.35",
1927
+ "date": "2026-05-08",
1928
+ "headline": "`b.tcpa10dlc` + `b.iabMspa` — TCPA 10DLC consent + IAB MSPA / GPP opt-out signals",
1929
+ "summary": "Two new compliance primitives. `b.tcpa10dlc` records the carrier-required consent fields for SMS / call campaigns at $500-$1,500 per-violation exposure. `b.iabMspa` decodes the IAB Multi-State Privacy Agreement / Global Privacy Platform universal opt-out signal so operators can refuse processing at the same point a CCPA \"do-not-sell\" header would.",
1930
+ "sections": [
1931
+ {
1932
+ "heading": "Added",
1933
+ "items": [
1934
+ {
1935
+ "title": "`b.tcpa10dlc.recordConsent` / `lookup` / `revoke`",
1936
+ "body": "TCPA 10DLC consent-record audit (47 USC §227 + 47 CFR §64.1200 + FCC 1:1 disclosure rule). `recordConsent({ phoneE164, brand, disclosureText, disclosurePartyKind, formUrl, ip, userAgent })` writes a tamper-evident audit row with the carrier-required fields; `lookup(phoneE164)` serves the carrier produce-on-demand workflow; `revoke(phoneE164, reason)` records consumer-initiated opt-out with audit trail."
1937
+ },
1938
+ {
1939
+ "title": "`b.iabMspa.parseGpp` / `checkOptOut` / `refuseProcessing`",
1940
+ "body": "Decodes the IAB Multi-State Privacy Agreement / Global Privacy Platform string framing (header + per-section payloads, section-id mapping for usnat / usca / usva / usco / usct / usut / usnv / usia / usde / usnj / ustx / usor / usmt / usnh). `checkOptOut(parsed, { dataUse, state })` returns `{ mustHonor, signals }` against operator-decoded section opt-outs (sale / sharing / targeted-ads / sensitive / child-data). `refuseProcessing(parsed, opts)` throws `IabMspaError` to halt the operator's data-flow."
1941
+ },
1942
+ {
1943
+ "title": "`b.iabMspa.gpcFromHeaders(req)`",
1944
+ "body": "Reads the W3C `Sec-GPC: 1` browser signal — universal opt-out per CCPA / CPRA §1798.135(b)(1) and similar state laws."
1945
+ }
1946
+ ]
1947
+ }
1948
+ ],
1949
+ "references": [
1950
+ {
1951
+ "label": "47 USC §227 TCPA",
1952
+ "url": "https://www.law.cornell.edu/uscode/text/47/227"
1953
+ },
1954
+ {
1955
+ "label": "47 CFR §64.1200",
1956
+ "url": "https://www.ecfr.gov/current/title-47/chapter-I/subchapter-B/part-64/subpart-L/section-64.1200"
1957
+ },
1958
+ {
1959
+ "label": "IAB Global Privacy Platform (GPP)",
1960
+ "url": "https://iabtechlab.com/gpp/"
1961
+ },
1962
+ {
1963
+ "label": "W3C Global Privacy Control",
1964
+ "url": "https://privacycg.github.io/gpc-spec/"
1965
+ }
1966
+ ]
1967
+ },
1968
+ {
1969
+ "version": "0.8.34",
1970
+ "date": "2026-05-08",
1971
+ "headline": "BiDi-strip regex rewritten in Unicode-escape form",
1972
+ "summary": "Cumulative republish of the v0.8.33 fixes. The v0.8.33 publish workflow blocked on ESLint `no-irregular-whitespace`; v0.8.34 rewrites the body-parser BiDi-strip regex in Unicode-escape form so the source no longer carries literal BiDi codepoints. The v0.8.33 tag exists in git but never reached npm.",
1973
+ "sections": [
1974
+ {
1975
+ "heading": "Fixed",
1976
+ "items": [
1977
+ {
1978
+ "title": "Body-parser BiDi-strip regex uses Unicode-escape form",
1979
+ "body": "`lib/middleware/body-parser.js` now expresses the BiDi-formatting codepoint set in `\\u202A`-style escapes instead of literal codepoints, so ESLint's `no-irregular-whitespace` rule passes during the publish workflow's lint gate."
1980
+ }
1981
+ ]
1982
+ }
1983
+ ]
1984
+ },
1985
+ {
1986
+ "version": "0.8.33",
1987
+ "date": "2026-05-08",
1988
+ "headline": "HTTP / web cleanup across WebSocket, Permissions-Policy, JWT, body-parser, OAuth",
1989
+ "summary": "Six RFC-cited refinements against shipped WebSocket, Permissions-Policy, JWT, body-parser, and OAuth surfaces.",
1990
+ "sections": [
1991
+ {
1992
+ "heading": "Fixed",
1993
+ "items": [
1994
+ {
1995
+ "title": "WebSocket close-frame validation against RFC 6455 §5.5.1 + §7.4.2",
1996
+ "body": "`_handleClose` now refuses 1-byte close-frame payloads (previously silently accepted as clean close, evading anomaly detection) and validates the close-code against the §7.4.2 vocabulary (1000-1011 + 3000-4999 valid; 1004 / 1005 / 1006 / 1015 reserved or forbidden; everything else refused)."
1997
+ },
1998
+ {
1999
+ "title": "JWT typ assertion (RFC 8725 §3.11)",
2000
+ "body": "`b.auth.jwt.verify({ expectedTyp })` refuses tokens whose `header.typ` doesn't match (typ-confusion class). Case-insensitive match per RFC 8725; unset `expectedTyp` skips the check for legacy token compatibility."
2001
+ },
2002
+ {
2003
+ "title": "Multipart filename BiDi / RTL strip (CVE-2021-42574)",
2004
+ "body": "`_sanitizeFilename` now drops Trojan Source BiDi formatting and zero-width codepoints from uploaded filenames before the basename hits the filesystem."
2005
+ },
2006
+ {
2007
+ "title": "OAuth `redirect_uri` localhost exception (RFC 9700 §4.1.1)",
2008
+ "body": "`b.auth.oauth.create({ redirectUri })` now accepts `http://localhost`, `http://127.0.0.1`, and `http://[::1]` without requiring `allowHttp: true` (which would loosen every operator-supplied URL). HTTPS still required for non-loopback hosts."
2009
+ }
2010
+ ]
2011
+ },
2012
+ {
2013
+ "heading": "Changed",
2014
+ "items": [
2015
+ {
2016
+ "title": "Permissions-Policy default expansion",
2017
+ "body": "`b.middleware.securityHeaders` deny-by-default now covers `interest-cohort`, `attribution-reporting`, `bluetooth`, `hid`, `serial`, `idle-detection`, `local-fonts`, `compute-pressure`, `window-management`, `private-state-token-issuance`, and `private-state-token-redemption`."
2018
+ }
2019
+ ]
2020
+ }
2021
+ ],
2022
+ "references": [
2023
+ {
2024
+ "label": "RFC 6455 WebSocket",
2025
+ "url": "https://www.rfc-editor.org/rfc/rfc6455.html"
2026
+ },
2027
+ {
2028
+ "label": "RFC 8725 JWT BCP",
2029
+ "url": "https://www.rfc-editor.org/rfc/rfc8725.html"
2030
+ },
2031
+ {
2032
+ "label": "RFC 9700 OAuth 2.0 Security BCP",
2033
+ "url": "https://www.rfc-editor.org/rfc/rfc9700.html"
2034
+ },
2035
+ {
2036
+ "label": "CVE-2021-42574 Trojan Source",
2037
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-42574"
2038
+ }
2039
+ ]
2040
+ },
2041
+ {
2042
+ "version": "0.8.32",
2043
+ "date": "2026-05-08",
2044
+ "headline": "Email / DNS impl-vs-spec cleanup across DKIM, SPF, DMARC, A-R, TLS-RPT, DoH",
2045
+ "summary": "Eight RFC-cited refinements against shipped DKIM, SPF, DMARC, Authentication-Results, TLS-RPT, and DoH primitives. No new surface — each item closes a wire-format gap surfaced by re-reading the source RFCs.",
2046
+ "sections": [
2047
+ {
2048
+ "heading": "Fixed",
2049
+ "items": [
2050
+ {
2051
+ "title": "DKIM verifier enforces `x=` expiration and `t=` future-date sanity",
2052
+ "body": "`b.mail.dkim` now permerrors signatures past their `x=` expiration (RFC 6376 §3.5) and refuses signatures more than 24h ahead of `now`; it also rejects `x= < t=` malformation. Operator-tunable `clockSkewMs` (default 5 min)."
2053
+ },
2054
+ {
2055
+ "title": "SPF distinguishes mechanisms from modifiers",
2056
+ "body": "`_parseSpfRecord` in `b.mail.spf` separates mechanisms from modifiers per RFC 7208 §4.6 — `redirect=` and `exp=` are modifiers, not mechanisms. Pre-release versions triggered the generic \"out of scope\" permerror; modifiers are now surfaced under `mechanisms.modifiers`."
2057
+ },
2058
+ {
2059
+ "title": "SPF IPv6 CIDR matching uses real bitwise check",
2060
+ "body": "`_ipv6Expand` plus group-by-group bit-mask comparison replaces the prior string-prefix heuristic that mishandled `::` shorthand. Aligns with RFC 7208's IPv6 mechanism semantics."
2061
+ },
2062
+ {
2063
+ "title": "DMARC consults `pct=` for sampled disposition",
2064
+ "body": "`b.mail.dmarc` `recommendedAction` now applies one-step-less-strict disposition when `pct < 100` per RFC 7489 §6.6.4 (reject → quarantine; quarantine → none) on the sampled fraction."
2065
+ },
2066
+ {
2067
+ "title": "Authentication-Results `reason=` escape preserves backslash",
2068
+ "body": "`b.mail.authResults` now serializes the quoted-string `reason=` using RFC 8601 §2.2 `\\\"` escape for lossless round-trip instead of the prior `\"`-to-`'` collapse."
2069
+ },
2070
+ {
2071
+ "title": "TLS-RPT validates `mailto:` rua before forwarding",
2072
+ "body": "`b.network.smtp.tlsRpt.submit` now RFC 5322 addr-spec-validates `mailto:` rua entries before forwarding to `b.mail`. Invalid mailto targets previously crashed at submit-time with opaque transport errors."
2073
+ },
2074
+ {
2075
+ "title": "DoH host validated label-by-label",
2076
+ "body": "`b.network.dns.resolveSecure` enforces the RFC 1035 §2.3.4 LDH rule (letters / digits / hyphen, no leading or trailing hyphen, label length 1..63). Underscore, colon, and space hosts previously flowed through to opaque DoH errors."
2077
+ }
2078
+ ]
2079
+ }
2080
+ ],
2081
+ "references": [
2082
+ {
2083
+ "label": "RFC 6376 DKIM",
2084
+ "url": "https://www.rfc-editor.org/rfc/rfc6376.html"
2085
+ },
2086
+ {
2087
+ "label": "RFC 7208 SPF",
2088
+ "url": "https://www.rfc-editor.org/rfc/rfc7208.html"
2089
+ },
2090
+ {
2091
+ "label": "RFC 7489 DMARC",
2092
+ "url": "https://www.rfc-editor.org/rfc/rfc7489.html"
2093
+ },
2094
+ {
2095
+ "label": "RFC 8601 Authentication-Results",
2096
+ "url": "https://www.rfc-editor.org/rfc/rfc8601.html"
2097
+ },
2098
+ {
2099
+ "label": "RFC 8460 TLS-RPT",
2100
+ "url": "https://www.rfc-editor.org/rfc/rfc8460.html"
2101
+ },
2102
+ {
2103
+ "label": "RFC 1035 DNS",
2104
+ "url": "https://www.rfc-editor.org/rfc/rfc1035.html"
2105
+ }
2106
+ ]
2107
+ },
2108
+ {
2109
+ "version": "0.8.31",
2110
+ "date": "2026-05-08",
2111
+ "headline": "`b.fdx` — CFPB §1033 / Financial Data Exchange consumer-financial-data wrapper",
2112
+ "summary": "CFPB §1033 (12 CFR §1033.121-461, final rule 2024-10-22) gives US consumers the right to authorize a third party to access their financial data through the data provider's developer interface. The compliance deadline for $250B+ asset-size banks has already passed (2026-04-01). The new `b.fdx` primitive composes onto `b.fapi2` (the §1033.351 security requirements track FAPI 2.0) and the FDX 6.0 minimum schema.",
2113
+ "sections": [
2114
+ {
2115
+ "heading": "Added",
2116
+ "items": [
2117
+ {
2118
+ "title": "`b.fdx.bind({ authServer, resources })`",
2119
+ "body": "Binds the operator's authorization server config to the FAPI 2.0 profile; refuses if PKCE / DPoP / PAR are misconfigured per `b.fapi2.assertOAuthConfig`. The §1033.351 security requirements track FAPI 2.0."
2120
+ },
2121
+ {
2122
+ "title": "`b.fdx.validateResponse(resourceType, body)`",
2123
+ "body": "Validates a response shape against the FDX 6.0 minimum schema for accounts, transactions, statements, payment-networks, rewards, and tax-forms — refuses missing-required fields."
2124
+ },
2125
+ {
2126
+ "title": "`b.fdx.consentReceipt(opts)`",
2127
+ "body": "Generates the §1033.401(b) consent receipt the authorization server gives the consumer at authorization time (data provider, consumer, third-party recipient, scopes, revocation URL, issued and expires timestamps)."
2128
+ }
2129
+ ]
2130
+ }
2131
+ ],
2132
+ "references": [
2133
+ {
2134
+ "label": "CFPB §1033 Final Rule (12 CFR §1033.121-461)",
2135
+ "url": "https://www.consumerfinance.gov/rules-policy/final-rules/required-rulemaking-on-personal-financial-data-rights/"
2136
+ },
2137
+ {
2138
+ "label": "Financial Data Exchange (FDX)",
2139
+ "url": "https://financialdataexchange.org/"
2140
+ }
2141
+ ]
2142
+ },
2143
+ {
2144
+ "version": "0.8.30",
2145
+ "date": "2026-05-08",
2146
+ "headline": "`b.aiPref` — AIPREF Content-Usage + Cloudflare Content Signals + Pay-Per-Crawl",
2147
+ "summary": "New primitive emits and parses the IETF AIPREF `Content-Usage` response header alongside Cloudflare's Content Signals Policy header, and serves HTTP 402 Payment Required for paid-crawl surfaces. Operators get a machine-readable channel to signal AI-training, AI-inference, and AI-snippet preferences ahead of the IETF spec finalizing.",
2148
+ "sections": [
2149
+ {
2150
+ "heading": "Added",
2151
+ "items": [
2152
+ {
2153
+ "title": "`b.aiPref.middleware({ train, infer, snippet, price? })`",
2154
+ "body": "Emits the `Content-Usage` header per request and (default-on) the Cloudflare-compatible `CF-Content-Signals` header alongside. Values: `allow` / `deny` / `paid` for train and infer; `allow` / `deny` for snippet."
2155
+ },
2156
+ {
2157
+ "title": "`b.aiPref.serializeHeader(opts)` and `b.aiPref.parseHeader(value)`",
2158
+ "body": "Round-trip codec for the `Content-Usage` header so operators can parse incoming crawler-stated preferences or serialize their own emit-side header outside the middleware path."
2159
+ },
2160
+ {
2161
+ "title": "`b.aiPref.robotsBlock(opts)`",
2162
+ "body": "Emits an AIPREF-compliant `User-agent: <bot>\\nContent-Usage: ...` block for inclusion in `robots.txt`, matching the working group's robots-side preference channel."
2163
+ },
2164
+ {
2165
+ "title": "`b.aiPref.refusePaidCrawl(req, res, { price })`",
2166
+ "body": "Emits HTTP 402 Payment Required with the price manifest in JSON for crawlers that hit a paid surface without a Pay-Per-Crawl token. Refused-crawl events emit an audit row."
2167
+ }
2168
+ ]
2169
+ }
2170
+ ],
2171
+ "references": [
2172
+ {
2173
+ "label": "draft-ietf-aipref-attach-04",
2174
+ "url": "https://datatracker.ietf.org/doc/draft-ietf-aipref-attach/"
2175
+ },
2176
+ {
2177
+ "label": "Cloudflare Content Signals Policy",
2178
+ "url": "https://blog.cloudflare.com/content-signals-policy/"
2179
+ }
2180
+ ]
2181
+ },
2182
+ {
2183
+ "version": "0.8.29",
2184
+ "date": "2026-05-08",
2185
+ "headline": "CI smoke heap bump for macOS-arm64 OOM",
2186
+ "summary": "The duplicate-block detector still accumulated roughly four million shingle-fingerprint entries across the `lib/` corpus, peaking above the Node default 2 GB heap on macOS-arm64 runners. The smoke gate is now configured with a higher heap ceiling; operators on memory-constrained machines have an explicit single-threaded fallback.",
2187
+ "sections": [
2188
+ {
2189
+ "heading": "Changed",
2190
+ "items": [
2191
+ {
2192
+ "title": "Smoke job heap ceiling raised to 4 GB",
2193
+ "body": "`.github/workflows/ci.yml` sets `NODE_OPTIONS: --max-old-space-size=4096` on the smoke step so the parallel-shingle-pass peak no longer trips the OOM killer on macOS-arm64 runners."
2194
+ },
2195
+ {
2196
+ "title": "Single-threaded fallback for memory-constrained hosts",
2197
+ "body": "Operators running the smoke suite locally on machines that can't afford the worker fan-out's peak set `HS_PATTERNS_NO_THREADS=1` to fall back to the single-threaded scan path."
2198
+ }
2199
+ ]
2200
+ },
2201
+ {
2202
+ "heading": "Fixed",
2203
+ "items": [
2204
+ {
2205
+ "title": "Hash-fingerprint duplicate-cluster experiment reverted",
2206
+ "body": "The in-progress hash-fingerprint experiment surfaced previously-grouped-by-shape duplicates as distinct hash-bucketed clusters that broke the existing `KNOWN_CLUSTERS` allowlist matching; the detector reverted to the join-on-space form."
2207
+ }
2208
+ ]
2209
+ }
2210
+ ]
2211
+ },
2212
+ {
2213
+ "version": "0.8.28",
2214
+ "date": "2026-05-08",
2215
+ "headline": "`b.contentCredentials` California SB-942 / AB-853 + C2PA 2.1 content-provenance manifest builder",
2216
+ "summary": "California Bus. & Prof. Code §22757 (effective 2026-08-02) requires providers of generative AI systems to embed a latent (machine-readable) provenance disclosure in every synthetic image / video / audio asset distributed in California. The disclosure MUST carry provider + system identifier + system version + content timestamp + unique content ID. SB-942 cites C2PA 2.1+ as an acceptable disclosure format.",
2217
+ "sections": [
2218
+ {
2219
+ "heading": "Added",
2220
+ "items": [
2221
+ {
2222
+ "title": "`b.contentCredentials.build(...)` builds an SB-942-shaped C2PA manifest",
2223
+ "body": "`b.contentCredentials.build({provider, system, systemVersion, contentId, contentType?, contentSha3?})` returns a frozen C2PA-shaped manifest with the SB-942 required fields, statutory citations, and an ISO-8601 generated timestamp. Operators feed the manifest into their existing muxer for embedding."
2224
+ },
2225
+ {
2226
+ "title": "`b.contentCredentials.sign(manifest, ...)` signs the canonical-JSON serialization",
2227
+ "body": "Signs over the canonical-JSON serialization of the manifest with the operator's audit-sign keypair (ML-DSA-87 by default). The signed envelope carries the manifest, signature, and algorithm metadata."
2228
+ },
2229
+ {
2230
+ "title": "`b.contentCredentials.verify(envelope, publicKeyPem)` validates signature + required fields",
2231
+ "body": "Validates the signature and refuses on any missing required field per SB-942 (provider / system / systemVersion / contentId / contentTimestamp). Returns the parsed manifest only when both checks pass."
2232
+ },
2233
+ {
2234
+ "title": "`b.contentCredentials.required(opts)` preflight helper",
2235
+ "body": "Returns the set of missing required-field tags for operator-side preflight before signing — surfaces gaps at integration time rather than at verify time."
2236
+ }
2237
+ ]
2238
+ },
2239
+ {
2240
+ "heading": "Changed",
2241
+ "items": [
2242
+ {
2243
+ "title": "Format embedding stays with the operator's muxer",
2244
+ "body": "The framework deliberately does NOT embed the manifest into image / video / audio bytes — the operator's muxer handles JPEG XMP / PNG iTXt / MP4 ContentBoxes per format. The framework owns manifest construction, signing, and verification only."
2245
+ }
2246
+ ]
2247
+ }
2248
+ ],
2249
+ "references": [
2250
+ {
2251
+ "label": "California Bus. & Prof. Code §22757 (SB-942 / AB-853)",
2252
+ "url": "https://leginfo.legislature.ca.gov/faces/billNavClient.xhtml?bill_id=202320240SB942"
2253
+ },
2254
+ {
2255
+ "label": "C2PA 2.1 specification",
2256
+ "url": "https://c2pa.org/specifications/specifications/2.1/index.html"
2257
+ }
2258
+ ]
2259
+ },
2260
+ {
2261
+ "version": "0.8.27",
2262
+ "date": "2026-05-08",
2263
+ "headline": "`b.fapi2` Financial-grade API 2.0 Final conformance posture",
2264
+ "summary": "Composes existing primitives (PAR / DPoP / OAuth 2.1 / mTLS) into a single boot-time conformance check against the FAPI 2.0 Final profile. No new crypto, no new HTTP endpoints — pure coordination of already-shipped surface.",
2265
+ "sections": [
2266
+ {
2267
+ "heading": "Added",
2268
+ "items": [
2269
+ {
2270
+ "title": "`b.fapi2.assertConformance({ senderConstraint })`",
2271
+ "body": "Returns `{ conformant, findings }` against the FAPI 2.0 Final profile: PAR required (§5.3.2.2), PKCE S256 (§5.3.1.1), sender-constrained tokens via DPoP OR mTLS (§5.3.2.5), issuer-in-callback per RFC 9207, JAR for signed-request envelopes. Operators run it at boot to refuse a misconfigured deployment fast."
2272
+ },
2273
+ {
2274
+ "title": "`b.fapi2.assertOAuthConfig(oauthOpts)`",
2275
+ "body": "Refuses to start a FAPI-bound deployment with PKCE disabled, with both DPoP and mTLS enabled (the profile requires exactly one sender-constraint), with neither DPoP nor mTLS enabled, or with PAR disabled. Refusal is at config time so the misconfig never reaches a live request."
2276
+ },
2277
+ {
2278
+ "title": "`fapi-2.0` posture registered with `b.compliance.set`",
2279
+ "body": "Operators activate the conformance posture via `b.compliance.set(\"fapi-2.0\")` (the posture identifier was registered alongside the broader compliance-posture catalog expansion in the previous release) and run `assertConformance` at boot. The posture lights up audit metadata and compliance dashboards without further wiring."
2280
+ }
2281
+ ]
2282
+ }
2283
+ ],
2284
+ "references": [
2285
+ {
2286
+ "label": "FAPI 2.0 Final — Security Profile",
2287
+ "url": "https://openid.net/specs/fapi-2_0-security-02.html"
2288
+ },
2289
+ {
2290
+ "label": "RFC 9207 OAuth 2.0 Authorization Server Issuer Identification",
2291
+ "url": "https://www.rfc-editor.org/rfc/rfc9207.html"
2292
+ }
2293
+ ]
2294
+ },
2295
+ {
2296
+ "version": "0.8.26",
2297
+ "date": "2026-05-08",
2298
+ "headline": "`b.iabTcf` IAB TCF v2.3 consent-string parser + disclosedVendors validator",
2299
+ "summary": "Operators feed inbound TC strings (from the `IAB-TC-String` cookie or query parameter) and gate ad-request paths. The framework refuses v2.2 strings that Google + DSPs already reject, and the codebase-patterns worker fan-out gains a memory-safe cap for macOS-arm64 CI runners.",
2300
+ "sections": [
2301
+ {
2302
+ "heading": "Added",
2303
+ "items": [
2304
+ {
2305
+ "title": "`b.iabTcf.parseString(tcString)`",
2306
+ "body": "Base64url-decoded segment-aware parse of an IAB TCF consent string. Returns `{ core, disclosedVendors, allowedVendors, publisherTC, errors }`. `core` carries `version`, `cmpId`, `vendorListVersion`, `policyVersion`, `vendorConsents` (Set of vendor IDs), `vendorLIs`, language, publisher country code, and created / lastUpdated timestamps. `disclosedVendors.vendorIds` is the Set of every vendor disclosed regardless of consent — REQUIRED by TCF Policy v2.3 §III.B.5 since 2026-02-28."
2307
+ },
2308
+ {
2309
+ "title": "`b.iabTcf.requireV23Disclosed(tcString)`",
2310
+ "body": "Refuses with `IabTcfError` on missing DisclosedVendors segment, wrong core version, or wrong policy version. Emits `iabtcf.refused` audit on rejection and `iabtcf.accepted` audit on pass, so the operator's compliance dashboard surfaces v2.2 stragglers without bespoke wiring."
2311
+ },
2312
+ {
2313
+ "title": "`b.iabTcf.checkVendor(parsed, vendorId)`",
2314
+ "body": "Returns `{ consented, legitimate, disclosed }` for a vendor id — operators gate per-vendor ad-request paths against a parsed TC string without re-walking the bitfield each call."
2315
+ }
2316
+ ]
2317
+ },
2318
+ {
2319
+ "heading": "Fixed",
2320
+ "items": [
2321
+ {
2322
+ "title": "codebase-patterns worker fan-out capped at 4 to fix macOS-arm64 OOM",
2323
+ "body": "Pre-0.8.26 the duplicate-block detector spawned `os.cpus().length` workers, each holding its per-shard fingerprint map in heap until message-resolve. macOS-arm64 CI runners (2 GB Node default heap) OOMed mid-scan starting v0.8.15 (every release since failed the macOS smoke job). Now capped at 4 workers; speedup preserved (5× single-threaded), peak memory bounded under 2 GB. Operators with bigger machines override via `HS_PATTERNS_WORKERS=N`."
2324
+ }
2325
+ ]
2326
+ }
2327
+ ],
2328
+ "references": [
2329
+ {
2330
+ "label": "IAB TCF v2.3 Policies",
2331
+ "url": "https://iabeurope.eu/iab-europe-transparency-consent-framework-policies/"
2332
+ },
2333
+ {
2334
+ "label": "IAB TCF Technical Specifications v2.3",
2335
+ "url": "https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/tree/main/TCFv2.3"
2336
+ }
2337
+ ]
2338
+ },
2339
+ {
2340
+ "version": "0.8.25",
2341
+ "date": "2026-05-08",
2342
+ "headline": "`b.secCyber.eightKArtifact` SEC Form 8-K Item 1.05 artifact generator",
2343
+ "summary": "Required by 17 CFR §229.106 / Form 8-K Item 1.05 (final rule effective 2023-12-18). When a registrant determines a cybersecurity incident is material, it MUST file a Form 8-K within 4 business days describing material aspects (nature / scope / timing) and material impact. The framework can't decide materiality but does structure the operator's materiality finding for filing.",
2344
+ "sections": [
2345
+ {
2346
+ "heading": "Added",
2347
+ "items": [
2348
+ {
2349
+ "title": "Materiality finding captured as a tamper-evident audit row",
2350
+ "body": "The operator's materiality determination is structured into a tamper-evident audit-chain row so the basis-for-filing is preserved for regulator and SOX-control review. The framework does NOT make the materiality call — that remains a fact-and-circumstances judgment owned by counsel."
2351
+ },
2352
+ {
2353
+ "title": "Form 8-K Item 1.05 narrative skeleton generator",
2354
+ "body": "Generates a markdown + JSON narrative skeleton matching the Item 1.05 required elements (nature / scope / timing / material impact) for downstream EDGAR filing. Operators flow it into their existing filer-attorney workflow; the framework does NOT submit to EDGAR."
2355
+ },
2356
+ {
2357
+ "title": "Four-business-day deadline computer",
2358
+ "body": "Computes the 4-business-day deadline from the materiality determination date so the operator's filing system can gate on it directly — the deadline becomes a queryable field rather than a calendar reminder."
2359
+ },
2360
+ {
2361
+ "title": "AG delay-request artifact under §229.106(c)(1)(ii)",
2362
+ "body": "Emits a US-AG delay-request artifact when the operator asserts national-security / public-safety risk under §229.106(c)(1)(ii). The artifact is operator-attested and audited; the framework does not transmit it."
2363
+ }
2364
+ ]
2365
+ }
2366
+ ],
2367
+ "references": [
2368
+ {
2369
+ "label": "17 CFR §229.106 / Form 8-K Item 1.05",
2370
+ "url": "https://www.ecfr.gov/current/title-17/chapter-II/part-229/subpart-229.100/section-229.106"
2371
+ },
2372
+ {
2373
+ "label": "SEC Final Rule — Cybersecurity Risk Management Disclosure",
2374
+ "url": "https://www.sec.gov/files/rules/final/2023/33-11216.pdf"
2375
+ }
2376
+ ]
2377
+ },
2378
+ {
2379
+ "version": "0.8.24",
2380
+ "date": "2026-05-08",
2381
+ "headline": "`b.budr` RTO/RPO declaration primitive + compliance posture registry expansion + literal-NUL detector",
2382
+ "summary": "Operator-attested business-continuity declarations land in the audit chain; the compliance-posture registry gains 19 new posture identifiers so operators can drive them through the framework's vocabulary; and a new codebase-patterns gate refuses any `lib/` source file containing a literal NUL byte — the failure class that blocked five previous releases at tag-push time.",
2383
+ "sections": [
2384
+ {
2385
+ "heading": "Added",
2386
+ "items": [
2387
+ {
2388
+ "title": "`b.budr.declare(opts)` RTO/RPO per-service declaration",
2389
+ "body": "Captures operator-attested Recovery Time Objective + Recovery Point Objective per service into a tamper-evident audit chain row. Required by DORA Article 11(1)(d) / ISO 22301:2019 / NIST SP 800-34. Tier vocabulary platinum / gold / silver / bronze; criticality critical / high / medium / low. `b.budr.list()` + `b.budr.get(service)` for dashboards."
2390
+ },
2391
+ {
2392
+ "title": "Compliance posture registry expanded",
2393
+ "body": "`b.compliance.set` now accepts `fapi-2.0`, `cfpb-1033`, `iab-tcf-v2.3`, `iab-mspa`, `tcpa-10dlc`, `fda-21cfr11`, `fda-annex-11`, `sec-1.05`, `ny-2-d`, `il-soppa`, `ca-sopipa`, `ct-pa-5-2`, `tx-hb-4504`, `va-sb-1376`, `staterramp`, `irap`, `bsi-c5`, `ens-es`, `uk-g-cloud`. Operators that previously hand-rolled posture identifiers in their app code now drive them through the framework's vocabulary and get the standard `compliance.posture.set` audit row."
2394
+ }
2395
+ ]
2396
+ },
2397
+ {
2398
+ "heading": "Detectors",
2399
+ "items": [
2400
+ {
2401
+ "title": "literal-NUL byte in `lib/` source refused at authoring time",
2402
+ "body": "New codebase-patterns gate refuses any `lib/` source file containing a literal NUL (0x00) byte. CI ESLint catches `no-control-regex` violations from a NUL byte inside a regex literal, but Windows local lint silently passed through (encoding-related). The new detector closes the v0.8.19–v0.8.23 publish-failure class where the npm-publish workflow blocked five releases on a NUL byte introduced by tooling-decoded JSON escapes. Operators and agents now see the failure at authoring time instead of at tag-push time."
2403
+ }
2404
+ ]
2405
+ }
2406
+ ],
2407
+ "references": [
2408
+ {
2409
+ "label": "DORA Article 11 — ICT business continuity policy",
2410
+ "url": "https://eur-lex.europa.eu/eli/reg/2022/2554/oj"
2411
+ },
2412
+ {
2413
+ "label": "ISO 22301:2019 — Business continuity management",
2414
+ "url": "https://www.iso.org/standard/75106.html"
2415
+ },
2416
+ {
2417
+ "label": "NIST SP 800-34 Rev. 1 — Contingency Planning",
2418
+ "url": "https://csrc.nist.gov/pubs/sp/800/34/r1/upd1/final"
2419
+ }
2420
+ ]
2421
+ },
2422
+ {
2423
+ "version": "0.8.23",
2424
+ "date": "2026-05-08",
2425
+ "headline": "`b.compliance.set` emits a TZ posture warning when `TZ` is not UTC",
2426
+ "summary": "When the operator activates a regulated posture (`hipaa` / `pci-dss` / `sox` / `gdpr` / `soc2` / `fda-21cfr11`) and `process.env.TZ` is set to a non-UTC value, the framework now emits a `compliance.posture.tz_warning` audit event with the recommendation to set `TZ=UTC`. Pure signal — no behavior change.",
2427
+ "sections": [
2428
+ {
2429
+ "heading": "Added",
2430
+ "items": [
2431
+ {
2432
+ "title": "`compliance.posture.tz_warning` audit event",
2433
+ "body": "Auditors expect timestamps in UTC; a non-UTC `TZ` doesn't break any framework primitive (the audit chain stores Unix-ms, and ISO-8601 emits use `toISOString()` which is always UTC). But operator code that calls `new Date(ts).toString()` or formats with non-UTC formatters can render confusing rows. The warning surfaces the configuration mismatch before the auditor finds it."
2434
+ }
2435
+ ]
2436
+ }
2437
+ ]
2438
+ },
2439
+ {
2440
+ "version": "0.8.22",
2441
+ "date": "2026-05-08",
2442
+ "headline": "`b.crypto.encrypt` deduplicates the hybrid-disabled audit emission",
2443
+ "summary": "Pre-0.8.22 every plain-KEM `encrypt()` call (operator passed `mlkemPub` only, no `ecPublicKey`) emitted a `system.crypto.hybrid_disabled` audit row — high-volume KEM-only deployments pegged the audit bus with redundant signal.",
2444
+ "sections": [
2445
+ {
2446
+ "heading": "Changed",
2447
+ "items": [
2448
+ {
2449
+ "title": "`system.crypto.hybrid_disabled` audited once per process",
2450
+ "body": "Now emitted ONCE per process. Operators who genuinely run KEM-only should call `b.crypto.encryptMlkemOnly` directly (which emits no audit on the hybrid leg); operators surprised by the absent hybrid leg still see the per-process signal so the misconfig is still surfaced."
2451
+ }
2452
+ ]
2453
+ }
2454
+ ]
2455
+ },
2456
+ {
2457
+ "version": "0.8.21",
2458
+ "date": "2026-05-08",
2459
+ "headline": "DB DDL audit + OTel `db.*` semantic conventions in audit metadata",
2460
+ "summary": "Schema mutations issued via `b.db.runSql` / `b.db.exec` now produce structured audit events with OTel-canonical attributes, so forensic review of framework-emitted DDL is no longer dependent on a custom wrapper.",
2461
+ "sections": [
2462
+ {
2463
+ "heading": "Added",
2464
+ "items": [
2465
+ {
2466
+ "title": "`b.db.runSql` / `b.db.exec` DDL audit emission",
2467
+ "body": "Every framework-emitted DDL statement (`CREATE` / `DROP` / `ALTER` / `TRUNCATE` / `RENAME` / `ATTACH` / `DETACH` / `REINDEX`) now records a `db.ddl.executed` audit event with success/failure outcome, statement-prefix preview (truncated 256 chars), and operation classifier. Pre-0.8.21 a `b.db.runSql(\"DROP TABLE users\")` produced zero audit signal — schema mutations were invisible to forensic review unless caught by a custom higher-level wrapper."
2468
+ },
2469
+ {
2470
+ "title": "OTel `db.*` semantic conventions in audit metadata",
2471
+ "body": "DDL audit metadata now emits `db.system: \"sqlite\"`, `db.operation: \"<KEYWORD>\"`, `db.statement: \"<truncated>\"` per the OpenTelemetry Spec semconv. Operators with OTel collectors see framework DDL events under the canonical attribute namespace without an adapter."
2472
+ }
2473
+ ]
2474
+ }
2475
+ ],
2476
+ "references": [
2477
+ {
2478
+ "label": "OpenTelemetry semconv — Database",
2479
+ "url": "https://opentelemetry.io/docs/specs/semconv/database/"
2480
+ }
2481
+ ]
2482
+ },
2483
+ {
2484
+ "version": "0.8.20",
2485
+ "date": "2026-05-08",
2486
+ "headline": "`b.vault.sealPemFile` hardening pass — size cap, TOCTOU defense, plaintext zero, callback audits, actor capture",
2487
+ "summary": "Five sub-class fixes against the auto-resealing PEM-file primitive shipped in v0.8.14. Adds a bounded source read, defeats a symlink TOCTOU window, zeroes plaintext after seal, audits operator-callback throws, and captures a forensic actor on `forceReseal`.",
2488
+ "sections": [
2489
+ {
2490
+ "heading": "Added",
2491
+ "items": [
2492
+ {
2493
+ "title": "`opts.maxSourceBytes` (default 1 MiB) caps source size",
2494
+ "body": "`lstat` runs first and refuses the source if its size exceeds `opts.maxSourceBytes` (default 1 MiB). Replaces the unbounded `fs.readFileSync(source)` — an operator with write access to the source path could previously present a 10 GiB file and OOM the host."
2495
+ }
2496
+ ]
2497
+ },
2498
+ {
2499
+ "heading": "Fixed",
2500
+ "items": [
2501
+ {
2502
+ "title": "Symlink TOCTOU between `lstat` and read closed",
2503
+ "body": "Symlink sources are now refused outright. The seal path then `open`s the file, runs `fstat`, and confirms the fd points at the same inode `lstat` saw — rejecting any mutation that lands between the `lstat` and the read."
2504
+ },
2505
+ {
2506
+ "title": "Plaintext PEM bytes zeroed on every code path",
2507
+ "body": "`safeBuffer.secureZero(plaintext)` now runs in a `finally` block so the operator-visible PEM bytes don't linger in the V8 heap after seal completes (or after a throw mid-seal)."
2508
+ },
2509
+ {
2510
+ "title": "Operator callback throws now audited instead of dropped",
2511
+ "body": "`onResealed` / `onError` callbacks that throw now emit `vault.seal_pem_file.on_resealed_callback_failed` / `on_error_callback_failed` audit events instead of being silently swallowed. The seal itself still completes; the operator's downstream wiring is no longer invisible when it fails."
2512
+ },
2513
+ {
2514
+ "title": "`forceReseal` captures forensic actor",
2515
+ "body": "`watcher.forceReseal({ actorId, reason })` now records the actor in the audit row — pre-0.8.20 the call was anonymous, a SOC 2 forensic-evidence gap. Watcher-driven resealings continue to record `actor: null` (kernel-event, no operator)."
2516
+ }
2517
+ ]
2518
+ }
2519
+ ]
2520
+ },
2521
+ {
2522
+ "version": "0.8.19",
2523
+ "date": "2026-05-08",
2524
+ "headline": "DB at-rest + Postgres connection hardening (SQLite PRAGMA defenses, connectAs validation, migrate-lock takeover audit)",
2525
+ "summary": "Sets `PRAGMA trusted_schema=OFF` + `PRAGMA cell_size_check=ON` for at-rest SQLite, validates Postgres GUC values for `Infinity` / `NaN` / NUL / CR / LF, and emits a tamper-evident takeover-audit event when the migrate runner force-replaces a stale lock.",
2526
+ "sections": [
2527
+ {
2528
+ "heading": "Changed",
2529
+ "items": [
2530
+ {
2531
+ "title": "SQLite PRAGMAs at boot",
2532
+ "body": "`b.db` boot sets `PRAGMA trusted_schema=OFF` (defends CVE-2018-8740 family — refuses to call functions / virtual-table modules from a malicious shadow schema; the attack vector is operator-supplied DB files: backups, restores from untrusted source) and `PRAGMA cell_size_check=ON` (refuses pages with corrupted cell sizes at parse time; cheap defense against malformed-page attacks). Both PRAGMAs are framework-default; operators don't wire them."
2533
+ },
2534
+ {
2535
+ "title": "`b.externalDb.adapters.connectAs` GUC validation",
2536
+ "body": "Numeric GUC values must now be `isFinite` (refuses `Infinity` / `NaN` at config time rather than emitting them in a SET that some Postgres builds parse-error on after the connection is already half-set). String GUC values now refuse embedded NUL / CR / LF (no legitimate use; would terminate the SET statement early in some drivers)."
2537
+ }
2538
+ ]
2539
+ },
2540
+ {
2541
+ "heading": "Added",
2542
+ "items": [
2543
+ {
2544
+ "title": "`b.externalDb.migrate.up/down` lock takeover audit",
2545
+ "body": "When the migrate runner force-replaces a stale lock (per `staleAfterMs`), the framework now emits an `externaldb.migrate.lock.takeover` audit event with the prior holder ID, takeover age, and the new holder. Pre-v0.8.19 the takeover happened silently — SOC2 forensic-evidence gap. The plain `lock.acquired` event still fires for non-takeover paths."
2546
+ }
2547
+ ]
2548
+ }
2549
+ ]
2550
+ },
2551
+ {
2552
+ "version": "0.8.18",
2553
+ "date": "2026-05-08",
2554
+ "headline": "Database query-builder hardening on `b.db.from(...).where|whereRaw` and `b.safeSql`",
2555
+ "summary": "Closes column-disclosure via LIKE-metachar leakage, array binding in IN, placeholder counting in raw SQL, and SQLite-specific PRAGMA / ATTACH escape vectors. Refuses `_blamejs_`-prefixed app schema names.",
2556
+ "sections": [
2557
+ {
2558
+ "heading": "Fixed",
2559
+ "items": [
2560
+ {
2561
+ "title": "`Query.where(field, \"LIKE\", value)` escapes wildcards",
2562
+ "body": "Now escapes SQL `%` and `_` wildcard metacharacters in operator-supplied values and emits `LIKE ? ESCAPE '\\\\'`. Closes the column-disclosure class where `q=%@%` enumerated the entire table. Operators who deliberately want LIKE wildcards bypass via `whereRaw()`."
2563
+ },
2564
+ {
2565
+ "title": "`Query.where(field, \"IN\", [...])` array binding",
2566
+ "body": "Expands an array to `IN (?, ?, ?)` and binds each value separately. Pre-v0.8.18 the array bound to a single placeholder and silently matched zero rows (`node:sqlite` `?` doesn't support array-binding). Refuses non-array / empty-array inputs."
2567
+ },
2568
+ {
2569
+ "title": "`Query.whereRaw(sql, params)` placeholder counting",
2570
+ "body": "Now skips `?` characters inside SQL string literals (single + double quoted, including `''`-doubled escapes), line comments (`-- ...`), and block comments (`/* ... */`). Pre-v0.8.18 the naive regex counted literal-`?` and either threw on count mismatch OR let through fragments with masked missing real placeholders."
2571
+ },
2572
+ {
2573
+ "title": "`b.safeSql.BANNED_IDENTIFIERS` extension",
2574
+ "body": "Refuses `pragma` / `attach` / `detach` / `analyze` / `vacuum` / `reindex` as bare identifiers. Closes the SQLite-specific escape-the-parameterized-model surface (PRAGMAs disable security-relevant protections; ATTACH mounts external databases)."
2575
+ },
2576
+ {
2577
+ "title": "`b.db.declareTable` framework-prefix collision",
2578
+ "body": "Refuses any app schema name that begins with the framework's `_blamejs_` prefix (was an exact-match Set; an app schema entry like `_blamejs_audit_log_archive` slipped past the gate and could shadow / look-alike framework tables)."
2579
+ }
2580
+ ]
2581
+ }
2582
+ ]
2583
+ },
2584
+ {
2585
+ "version": "0.8.17",
2586
+ "date": "2026-05-08",
2587
+ "headline": "Email auth + DANE / TLS-RPT spec-conformance fixes (ARC / DMARC / SPF / A-R / DANE / TLS-RPT)",
2588
+ "summary": "Ten more RFC-cited gaps closed against shipped ARC (RFC 8617), DMARC (RFC 7489), SPF (RFC 7208), Authentication-Results (RFC 8601), DANE (RFC 6698 / 7672), and TLS-RPT (RFC 8460). Bug fixes only — no new operator-facing primitives.",
2589
+ "sections": [
2590
+ {
2591
+ "heading": "Fixed",
2592
+ "items": [
2593
+ {
2594
+ "title": "ARC (`b.mail.arc`) AAR canonicalization + time-window enforcement",
2595
+ "body": "Signer's AMS canonicalization now includes the current hop's `ARC-Authentication-Results` per RFC 8617 §5.1.1 (auto-prepends to `h=` unless operator passes `excludeAarFromAms: true`). Microsoft + Google receivers DO include AAR in `h=`; the prior framework default produced chains those receivers couldn't verify. Verifier counterpart: when reconstructing the AMS canonical input, only the CURRENT hop's AAR is kept (prior-hop AARs stripped). Verifier now enforces `t=` (signing time) + `x=` (expiration) time windows per §5.2 with operator-tunable `clockSkewMs` (default 5 min) — pre-v0.8.17 the verifier parsed but never enforced."
2596
+ },
2597
+ {
2598
+ "title": "DMARC (`b.mail.dmarc`) multi-record + subdomain `sp=` fallback",
2599
+ "body": "Multiple `v=DMARC1` records on a domain now treated as having no DMARC record per RFC 7489 §6.6.3. Subdomain `sp=` fallback wired: when `_dmarc.<from-domain>` returns no record, the verifier walks one label up to the (heuristic) organizational domain and applies its `sp=` — falls back to `p=` when `sp=` absent. Result includes `policyOriginDomain` + `orgDomainPolicyApplied: true`. Heuristic only — operators with PSL needs supply their own `dnsLookup` for full Public Suffix List walk."
2600
+ },
2601
+ {
2602
+ "title": "SPF (`b.mail.spf`) initial-query lookup count",
2603
+ "body": "Initial query for the sender's SPF record no longer counts toward the 10-lookup limit per RFC 7208 §4.6.4. Pre-v0.8.17 was off-by-one — senders at the spec ceiling got false `permerror`."
2604
+ },
2605
+ {
2606
+ "title": "Authentication-Results (`b.mail.authResults`) method-specific vocabulary",
2607
+ "body": "Result vocabulary is now METHOD-SPECIFIC per RFC 8601 §2.7 (per-method `AR_RESULTS_BY_METHOD` map). The flat `AR_VALID_RESULTS` table previously accepted `hardfail` for DKIM (only valid for DMARC §2.7.4) and accepted `temperror` / `permerror` for methods that don't recognize them."
2608
+ },
2609
+ {
2610
+ "title": "DANE (`b.network.smtp.dane`) DNSSEC + chain-order enforcement",
2611
+ "body": "`daneTlsa()` refuses to return records unless the caller passes `opts.dnssecValidated: true` per RFC 7672 §1.3 (TLSA records that are not DNSSEC-validated MUST NOT be used). `daneVerifyChain` enforces RFC 6698 §2.1.4 + RFC 7672 §3.1.1 chain-order: a DANE-TA match at chain position `i` is accepted only when its DER Subject equals the DER Issuer of cert at position `i-1`. Chain-order is best-effort — synthetic test fixtures without ASN.1-parseable DER fall through with `chainOrderUnverified: true` flagged on the match."
2612
+ },
2613
+ {
2614
+ "title": "TLS-RPT (`b.network.smtp.tlsRpt.fetchPolicy`) `rua=` requirement",
2615
+ "body": "Record without `rua=` now returns `null` (record ignored) per RFC 8460 §3. Pre-v0.8.17 returned `{ version: \"TLSRPTv1\", rua: [] }` which operators incorrectly treated as a valid record with no destinations."
2616
+ }
2617
+ ]
2618
+ }
2619
+ ],
2620
+ "references": [
2621
+ {
2622
+ "label": "RFC 8617 ARC",
2623
+ "url": "https://www.rfc-editor.org/rfc/rfc8617.html"
2624
+ },
2625
+ {
2626
+ "label": "RFC 7489 DMARC",
2627
+ "url": "https://www.rfc-editor.org/rfc/rfc7489.html"
2628
+ },
2629
+ {
2630
+ "label": "RFC 8601 Authentication-Results",
2631
+ "url": "https://www.rfc-editor.org/rfc/rfc8601.html"
2632
+ },
2633
+ {
2634
+ "label": "RFC 6698 DANE",
2635
+ "url": "https://www.rfc-editor.org/rfc/rfc6698.html"
2636
+ },
2637
+ {
2638
+ "label": "RFC 7672 SMTP / DANE",
2639
+ "url": "https://www.rfc-editor.org/rfc/rfc7672.html"
2640
+ },
2641
+ {
2642
+ "label": "RFC 8460 TLS-RPT",
2643
+ "url": "https://www.rfc-editor.org/rfc/rfc8460.html"
2644
+ }
2645
+ ]
2646
+ },
2647
+ {
2648
+ "version": "0.8.16",
2649
+ "date": "2026-05-08",
2650
+ "headline": "Email auth + transport spec-conformance fixes (DKIM / SPF / MTA-STS / OCSP)",
2651
+ "summary": "Closes ten RFC-cited gaps against shipped DKIM (RFC 6376), SPF (RFC 7208), MTA-STS (RFC 8461), and OCSP (RFC 6960) primitives. Bug fixes only — no new operator-facing primitives, no surface change beyond the additional refusals.",
2652
+ "sections": [
2653
+ {
2654
+ "heading": "Fixed",
2655
+ "items": [
2656
+ {
2657
+ "title": "DKIM verifier (`b.mail.dkim`)",
2658
+ "body": "Refuses any signature whose `h=` tag does not include `from` (RFC 6376 §3.5 cornerstone bypass — without From-coverage the signature does not bind to the visible sender). Refuses unrecognized `v=` tag values per §3.5 (only `v=1` accepted). Enforces empty `p=` as explicit key revocation per §3.6.1 (verdict `fail`, not `permerror`). Enforces `k=` algorithm-family tag agreement with the signature's `a=` family (e.g. `k=rsa` paired with `a=ed25519-sha256` permerrors per §3.6.1). Selector validator accepts multi-label selectors per §3.1 ABNF (common for time-rotated keys like `2024.s1`)."
2659
+ },
2660
+ {
2661
+ "title": "SPF (`b.mail.spf`)",
2662
+ "body": "Refuses domains that publish multiple `v=spf1` TXT records with `permerror` per RFC 7208 §4.5 (most operators don't realize multi-record SPF was always invalid). `include:` mechanism now permerrors when the included domain has no SPF record per §5.2 (closes the silent-authorization class where `include:gone-domain.example` followed by `+all` would silently allow)."
2663
+ },
2664
+ {
2665
+ "title": "MTA-STS (`b.smtpPolicy.mtaSts.fetch`)",
2666
+ "body": "Requires the `_mta-sts.<domain>` TXT precondition record per RFC 8461 §3.1 before fetching the HTTPS policy (closes the silent-escalation class). Cache TTL is now bounded by the policy's `max_age` value per §3.2 (clamped between 1 hour floor and 1 year ceiling) instead of the framework's hardcoded 60-min default."
2667
+ },
2668
+ {
2669
+ "title": "OCSP (`b.network.tls.ocsp.evaluate`)",
2670
+ "body": "`evaluateOcspResponse` enforces the `thisUpdate` / `nextUpdate` time window per RFC 6960 §4.2.2.1, rejecting responses whose validity window has expired or hasn't started yet (with operator-tunable `clockSkewMs`, default 5 min). Pre-v0.8.16 a captured \"good\" response could replay forever even after the cert was revoked; this defeats `requireGood` posture."
2671
+ }
2672
+ ]
2673
+ }
2674
+ ],
2675
+ "references": [
2676
+ {
2677
+ "label": "RFC 6376 DKIM",
2678
+ "url": "https://www.rfc-editor.org/rfc/rfc6376.html"
2679
+ },
2680
+ {
2681
+ "label": "RFC 7208 SPF",
2682
+ "url": "https://www.rfc-editor.org/rfc/rfc7208.html"
2683
+ },
2684
+ {
2685
+ "label": "RFC 8461 MTA-STS",
2686
+ "url": "https://www.rfc-editor.org/rfc/rfc8461.html"
2687
+ },
2688
+ {
2689
+ "label": "RFC 6960 OCSP",
2690
+ "url": "https://www.rfc-editor.org/rfc/rfc6960.html"
2691
+ }
2692
+ ]
2693
+ },
2694
+ {
2695
+ "version": "0.8.15",
2696
+ "date": "2026-05-08",
2697
+ "headline": "Transport-layer CVE absorption + AI-protocol primitives + WebSocket / TLS / httpClient hardening",
2698
+ "summary": "Ships SSE / MCP / GraphQL-federation / `b.ai.input.classify` / A2A / dark-patterns primitives, closes a WebSocket control-frame amplification class (RFC 6455 §5.5), absorbs Node CVE-2026-21710 / 21637 via defensive accessors, lifts the inbound TLS default to TLS 1.3, defaults httpClient to `Accept-Encoding: identity`, and lifts the Node engine pin to 24.4.0 for the undici fix.",
2699
+ "sections": [
2700
+ {
2701
+ "heading": "Added",
2702
+ "items": [
2703
+ {
2704
+ "title": "`b.sse` — Server-Sent Events transport",
2705
+ "body": "Newline-injection refusal in `event:` / `id:` / `data:` fields and the `Last-Event-ID` reconnect header (CVE-2026-33128 h3, CVE-2026-29085 Hono, CVE-2026-44217 sse-channel). `channel.send({event, id, data, retry})` validates each field, refuses LF/CR/NUL, splits multi-line `data` into per-spec multiple `data:` lines, drives a `:keepalive` heartbeat with operator-tunable interval. `b.sse.serializeEvent({...})` exposes the encoder for buffered pipelines."
2706
+ },
2707
+ {
2708
+ "title": "`b.mcp.serverGuard` — Model Context Protocol hardening",
2709
+ "body": "Bearer auth required by default (CVE-2026-33032 nginx-ui auth-bypass class). `redirect_uri` exact-match allowlist enforced per OAuth 2.1 / RFC 9700 §4.1.1 (CVE-2025-6514 mcp-remote OAuth RCE class). Dynamic client-registration refused unless `allowDynamicRegister: true` with operator-supplied registration allowlist (confused-deputy class). Tool/resource name allowlists at the guard layer. JSON-RPC 2.0 envelope validator."
2710
+ },
2711
+ {
2712
+ "title": "`b.graphqlFederation.guardSdl`",
2713
+ "body": "Refuses `_service.sdl` / `_entities` probes without a router-token Bearer + optional single-use nonce; closes the schema-leak class where operators disable introspection thinking the schema is hidden."
2714
+ },
2715
+ {
2716
+ "title": "`b.ai.input.classify` — pattern-based prompt-injection classifier",
2717
+ "body": "Covers OWASP LLM01:2025 + NIST COSAIS RFI shapes: instruction-override / persona-jailbreak / role-reset markers / vendor system-tag templates / tool-call injection / exfil-callback / encoded-bypass (base64/rot13) / markdown+HTML smuggling / BIDI / zero-width / control char density. Severity-3 hits → `verdict: \"malicious\"`; 2+ severity-2 hits → `\"suspicious\"`; otherwise `\"clean\"`. Inline-on-every-request perf cost — no model call, no network."
2718
+ },
2719
+ {
2720
+ "title": "`b.a2a` — signed agent-card primitive",
2721
+ "body": "A2A (Linux Foundation Agentic AI Foundation) v1.x. `signCard` produces an envelope with a detached ML-DSA-87 signature over the SHA3-512 of the canonical-JSON serialization (RFC 8785-aligned); `verifyCard` validates signature + expiry + issuer match. Endpoints HTTPS-only (or localhost) at validation time."
2722
+ },
2723
+ {
2724
+ "title": "`b.darkPatterns` — FTC click-to-cancel parity attestation",
2725
+ "body": "`recordSignupFlow` / `recordCancelFlow` capture operator-attested click counts, CTA contrast / font weight, channel, confirmation steps; `assertParity` returns `{ ok, breaches }` against `ftc-2024` / `ca-sb942` / `strict` postures. `middleware({lookupAttestation, resourceIdFromReq})` refuses cancel-endpoint requests with HTTP 451 if no parity attestation is on file."
2726
+ },
2727
+ {
2728
+ "title": "`b.requestHelpers.safeHeadersDistinct(req)`",
2729
+ "body": "Defensive accessor for `req.headersDistinct` that bypasses Node's faulty getter (Node CVE-2026-21710 — reading `__proto__` on the underlying header bag throws synchronously inside the getter, escaping handler-level try/catch). Computes the same null-prototype shape directly from `req.rawHeaders`."
2730
+ }
2731
+ ]
2732
+ },
2733
+ {
2734
+ "heading": "Changed",
2735
+ "items": [
2736
+ {
2737
+ "title": "Inbound TLS default lifted to TLS 1.3",
2738
+ "body": "`router.listen()` now defaults to `minVersion: \"TLSv1.3\"` when the operator's `tlsOptions` doesn't pin one (closes the gap where bare `{key, cert}` inherited Node's TLSv1.2 default)."
2739
+ },
2740
+ {
2741
+ "title": "`router.listen()` wraps SNICallback",
2742
+ "body": "Synchronous throws (Node CVE-2026-21637) become a clean async `(err, null)` callback rather than crashing the listener."
2743
+ },
2744
+ {
2745
+ "title": "`b.httpClient` defaults to `Accept-Encoding: identity`",
2746
+ "body": "Refuses compressed responses unless the operator explicitly opts in. Closes the undici unbounded-decompression amplification class (CVE-2026-22036). Operators that need compressed responses pass an explicit `Accept-Encoding` header."
2747
+ },
2748
+ {
2749
+ "title": "`engines.node` raised from `>=24.0.0` to `>=24.4.0`",
2750
+ "body": "Ensures the undici fix is bundled."
2751
+ }
2752
+ ]
2753
+ },
2754
+ {
2755
+ "heading": "Fixed",
2756
+ "items": [
2757
+ {
2758
+ "title": "WebSocket control-frame size cap (RFC 6455 §5.5)",
2759
+ "body": "`lib/websocket.js` `_handleFrame` refuses any control frame (opcodes ≥ 0x8: CLOSE/PING/PONG) with payload length > 125 or `fin = false`. Closes the 2× outbound-bandwidth amplification class where a 1 MiB PING was echoed verbatim as PONG."
2760
+ }
2761
+ ]
2762
+ }
2763
+ ],
2764
+ "references": [
2765
+ {
2766
+ "label": "CVE-2026-33128 h3",
2767
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33128"
2768
+ },
2769
+ {
2770
+ "label": "CVE-2026-29085 Hono",
2771
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-29085"
2772
+ },
2773
+ {
2774
+ "label": "CVE-2026-22036 undici",
2775
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22036"
2776
+ },
2777
+ {
2778
+ "label": "CVE-2026-21710 Node",
2779
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-21710"
2780
+ },
2781
+ {
2782
+ "label": "CVE-2026-21637 Node SNI",
2783
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-21637"
2784
+ }
2785
+ ]
2786
+ },
2787
+ {
2788
+ "version": "0.8.14",
2789
+ "date": "2026-05-07",
2790
+ "headline": "`b.vault.sealPemFile` — auto-resealing wrapper for at-rest PEM files",
2791
+ "summary": "Closes the cleartext-PEM window between ACME / Let's Encrypt renewals and manual re-seal. Watches the source via `fs.watchFile`, atomically re-seals on mtime change, and exposes a crash-recovery marker so a half-written reseal completes idempotently on restart.",
2792
+ "sections": [
2793
+ {
2794
+ "heading": "Added",
2795
+ "items": [
2796
+ {
2797
+ "title": "`b.vault.sealPemFile({ source, destination })`",
2798
+ "body": "Operators with ACME / Let's Encrypt renewals get fresh certs every 30-60 days; the renewal writes plaintext PEM to disk, signals the application to reload, and leaves the cleartext file unencrypted between the renewal write and the next manual re-seal. `sealPemFile` reads the source, vault-seals it, atomically writes `<destination>` (`.tmp` + `fsync` + `rename` + `fsyncDir`), and registers an `fs.watchFile` poll on the source. Every mtime change triggers an automatic re-seal — the operator-visible `<destination>.rewriting` marker is created before the rename and removed after, giving crash recovery a signal: when `sealPemFile()` starts and the marker is present, it re-seals from source idempotently. Returns `{ stop, generation, lastResealedAt, lastError, watching, forceReseal }`. `pollInterval` defaults to 2s — ACME renewal cadence is days, so polling latency is irrelevant against the renewal interval. `fs.watchFile` (the polling backend) is used instead of `fs.watch` (inotify / kqueue) because watchFile is consistent across platforms — Linux fires multiple change events per rename, macOS doesn't fire on renamed-into files, and the polling cadence is acceptable here."
2799
+ }
2800
+ ]
2801
+ }
2802
+ ]
2803
+ },
2804
+ {
2805
+ "version": "0.8.13",
2806
+ "date": "2026-05-07",
2807
+ "headline": "Streaming multipart uploads + `onChunk` response hook on `b.httpClient`",
2808
+ "summary": "Closes the `Buffer.concat` OOM class on large uploads via three new entry shapes (in-memory, disk-stream, operator-stream) and adds an `onChunk(chunk)` hook that fires for each response chunk in both buffer and stream modes — hash bytes while piping without an extra Transform pass.",
2809
+ "sections": [
2810
+ {
2811
+ "heading": "Added",
2812
+ "items": [
2813
+ {
2814
+ "title": "Streaming multipart on `b.httpClient.request({ multipart: { files: [...] } })`",
2815
+ "body": "Three file-entry shapes: existing `{ field, content: Buffer | string }` (in-memory), new `{ field, filePath: string }` (stream-from-disk via `fs.createReadStream`), and `{ field, stream: Readable, size?: number }` (operator-supplied stream). When every entry's size is statically resolvable (Buffer length / `fs.statSync().size` / explicit `opts.size`), the framework sets `Content-Length` and uses identity transfer; otherwise the framework omits the header and Node's HTTP layer falls back to chunked transfer. Body is materialized one chunk at a time through a `Readable.from(asyncIterator)` that yields boundary headers, source bytes, and CRLF in order. Fast-path preserved: when no streaming source is involved, `_buildMultipartBody` still returns a single Buffer with a known `Content-Length`."
2816
+ },
2817
+ {
2818
+ "title": "`onChunk(chunk)` response hook",
2819
+ "body": "Fires for each response data chunk in BOTH `responseMode: \"buffer\"` and `responseMode: \"stream\"`. Use case: hash bytes during pipe-to-disk without an extra Transform pass (`onChunk: (c) => hasher.update(c)`). Throws inside the hook are caught and dropped — a hash-mismatch detector can raise without breaking the pipe; callers surface the error through their own pipe handler."
2820
+ }
2821
+ ]
2822
+ },
2823
+ {
2824
+ "heading": "Security",
2825
+ "items": [
2826
+ {
2827
+ "title": "Verified shipped: `b.httpClient` + `b.cryptoField` + `b.config` redaction",
2828
+ "body": "Re-verified during the framework-gap audit: `b.httpClient` `responseMode: \"always-resolve\"`, `onRedirect({from, to, hop, headersStripped, statusCode})` hook, `body: Readable` upload path; `b.cryptoField` derived-hash domain separation (`bj-<table>-<field>:` per-field namespace prefix matches the indexed-lookup requirement); `b.config` `redactKeys` allowlist + `redacted()` view."
2829
+ }
2830
+ ]
2831
+ }
2832
+ ]
2833
+ },
2834
+ {
2835
+ "version": "0.8.12",
2836
+ "date": "2026-05-07",
2837
+ "headline": "WebSocket upgrade refuses credential-shaped query parameters by default",
2838
+ "summary": "URL query strings leak through access logs, browser history, Referer to third-party CDN / analytics, in-process / proxy captures, and crash dumps — RFC 6750 §2.3 explicitly cautions against bearer tokens in URI query parameters. `validateUpgradeRequest` now refuses upgrades carrying a credential-shaped query parameter; operators with a legitimate non-credential collision opt out per route.",
2839
+ "sections": [
2840
+ {
2841
+ "heading": "Changed",
2842
+ "items": [
2843
+ {
2844
+ "title": "`validateUpgradeRequest(req, opts)` credential-query refusal",
2845
+ "body": "Scans the request URL for the credential-leak names `access_token`, `bearer`, `bearer_token`, `apikey`, `api_key`, `api-key`, `authorization` (case-insensitive, with percent-decoding) and refuses the upgrade with HTTP 400 when one is present. Operators with a non-credential parameter that happens to share a credential-shaped name opt out per route via `opts.allowQueryAuthParams: true` with an audited operator reason. The refused list is deliberately narrow: overloaded names (`token`, `auth`, `key`, `session`) have non-credential meanings (CSRF tokens, file-share tokens, session-resume identifiers) and are NOT refused."
2846
+ }
2847
+ ]
2848
+ }
2849
+ ],
2850
+ "references": [
2851
+ {
2852
+ "label": "RFC 6750 §2.3 Bearer Tokens in URI Queries",
2853
+ "url": "https://www.rfc-editor.org/rfc/rfc6750.html#section-2.3"
2854
+ }
2855
+ ]
2856
+ },
2857
+ {
2858
+ "version": "0.8.11",
2859
+ "date": "2026-05-07",
2860
+ "headline": "All-50-states breach-deadline registry + adverse-decision wrapper + age-gate + per-primitive test-coverage gate",
2861
+ "summary": "Adds `b.breach.deadline` + `b.breach.report` for multi-state data-breach notification, `b.ai.adverseDecision` for GDPR-22 / EU AI Act / ECOA / Colorado / NYC-144 / FCRA consumer-rights wrapping, `b.middleware.ageGate` for COPPA / AADC postures, and a release gate that refuses operator-facing primitives without at least one test reference.",
2862
+ "sections": [
2863
+ {
2864
+ "heading": "Added",
2865
+ "items": [
2866
+ {
2867
+ "title": "`b.breach.deadline` + `b.breach.report`",
2868
+ "body": "All-50-states data-breach-notification deadline registry. `b.breach.deadline.forStates(states, detectedAt)` returns per-state `{ state, kind, dueBy, citation }` records (`kind: \"as-soon-as-possible\"` for AS-OF / `\"hard-deadline\"` for fixed-day deadlines like Texas / Florida / Maine). `b.breach.report.create()` opens a multi-state breach with a single record, tracks per-state filings via `fileNotice(id, state, ...)`, exposes `pending(id)` for dashboards, and auto-closes once every affected state has filed. Statutory citations + day counts wired in `lib/breach-deadline.js` per-state."
2869
+ },
2870
+ {
2871
+ "title": "`b.ai.adverseDecision`",
2872
+ "body": "Wraps an operator-supplied `decide(subject)` predicate; automatically attaches a consumer-rights notice when the outcome is `\"adverse\"` / `\"denied\"` / `\"rejected\"`. Built-in regulation templates for `gdpr-22`, `ai-act-86`, `ecoa-1002.9`, `colorado-ai-act`, `nyc-ll-144`, `fcra-615`, `operator-defined`. Notice carries `principalReasons` + `consumerRights: { requestData, requestExplanation, contestDecision, requestHumanReview }` shaped per regime."
2873
+ },
2874
+ {
2875
+ "title": "`b.middleware.ageGate`",
2876
+ "body": "Request-level age-classification middleware. Operator-supplied `getAge(req)` returns the subject age (or null/undefined when unknown); middleware classifies as `\"above-threshold\"` / `\"below-threshold\"` / `\"unknown\"` against `consentRequired`, sets `X-Privacy-Posture` header, and refuses with 451 + audited reason when `requireAge` is set and `hasParentalConsent(req)` is unmet. Composes upstream of session / authn for COPPA / AADC / UK Children's Code postures."
2877
+ }
2878
+ ]
2879
+ },
2880
+ {
2881
+ "heading": "Detectors",
2882
+ "items": [
2883
+ {
2884
+ "title": "Per-primitive test-coverage gate",
2885
+ "body": "New `test/layer-0-primitives/test-coverage.test.js` walks every operator-facing `b.*` primitive and refuses release unless the primitive has at least one test reference (or an explicit `UNTESTED_BACKLOG` entry naming the reason). Closes the drift class where a primitive landed on `b.*` but never gained a unit test."
2886
+ }
2887
+ ]
2888
+ }
2889
+ ]
2890
+ },
2891
+ {
2892
+ "version": "0.8.10",
2893
+ "date": "2026-05-07",
2894
+ "headline": "Five compliance / regulatory primitives composing on `b.incident.report`",
2895
+ "summary": "Adds CRA Article 14 + NIS2 Article 23 incident wrappers, a GDPR Article 30 RoPA registry + exporter, an EU Accessibility Act conformance generator, and a California SB 1001 bot-disclosure middleware.",
2896
+ "sections": [
2897
+ {
2898
+ "heading": "Added",
2899
+ "items": [
2900
+ {
2901
+ "title": "`b.cra.report` — EU Cyber Resilience Act Article 14 wrapper",
2902
+ "body": "Three-stage statutory deadlines: 24h early warning / 72h incident notification / 14d final report. Required `productId` + `manufacturer` per Annex VII §1. Optional ENISA submission via `opts.enisaEndpoint` + `b.httpClient`; submission is operator-opt-in per stage call (regulators uniformly require operator review before filing)."
2903
+ },
2904
+ {
2905
+ "title": "`b.nis2.report` — NIS2 Article 23 wrapper",
2906
+ "body": "Three-stage deadlines: 24h / 72h / 1 month. Annex I (essential) / Annex II (important) entity classification + sector codes (`I.6` drinking water / `II.6` digital providers / etc.)."
2907
+ },
2908
+ {
2909
+ "title": "`b.gdpr.ropa` — GDPR Article 30 RoPA registry + exporter",
2910
+ "body": "Validates required fields per Article 30 §1; legal-basis enum per Article 6(1); produces a regulator-friendly RoPA document for the operator's DPO to file. JSON / CSV / Markdown export."
2911
+ },
2912
+ {
2913
+ "title": "`b.compliance.eaa` — EU Accessibility Act Article 13",
2914
+ "body": "Declared-conformance generator. Operators declare per-criterion conformance against WCAG 2.1/2.2 AA / EN 301 549; non-conformances ship with reason + mitigation. JSON / Markdown export for the operator's accessibility statement."
2915
+ },
2916
+ {
2917
+ "title": "`b.middleware.botDisclose` — California SB 1001",
2918
+ "body": "Cal. Bus. & Prof. Code §17941 bot-disclosure middleware. Injects a disclosure banner into HTML responses, sets `X-Bot-Disclosure` header for API consumers, and audits every conversation-initiating request. Operators wire `mountPaths` to scope and `bannerHtml` for visual customization."
2919
+ }
2920
+ ]
2921
+ }
2922
+ ],
2923
+ "references": [
2924
+ {
2925
+ "label": "EU Cyber Resilience Act (Regulation 2024/2847)",
2926
+ "url": "https://eur-lex.europa.eu/eli/reg/2024/2847/oj"
2927
+ },
2928
+ {
2929
+ "label": "NIS2 Directive (Directive 2022/2555)",
2930
+ "url": "https://eur-lex.europa.eu/eli/dir/2022/2555/oj"
2931
+ },
2932
+ {
2933
+ "label": "EU Accessibility Act (Directive 2019/882)",
2934
+ "url": "https://eur-lex.europa.eu/eli/dir/2019/882/oj"
2935
+ }
2936
+ ]
2937
+ },
2938
+ {
2939
+ "version": "0.8.9",
2940
+ "date": "2026-05-07",
2941
+ "headline": "`b.incident.report` — generic 3-stage incident-reporting primitive",
2942
+ "summary": "Composes the three deadline pattern that recurs across regulatory regimes (initial / intermediate / final report) with built-in per-regime deadlines for GDPR Art. 33, NIS2 Art. 23, DORA Art. 19, CRA Art. 14, and HIPAA Breach Notification, plus `late: bool` + `lateBy: ms` on every record.",
2943
+ "sections": [
2944
+ {
2945
+ "heading": "Added",
2946
+ "items": [
2947
+ {
2948
+ "title": "`b.incident.report`",
2949
+ "body": "Three stages mirror the deadline pattern across regulatory regimes: initial / early-warning notification (within 24h of detection), intermediate / status update (within 72h), final report (within 30d or per-regime deadline). Built-in per-regime deadlines for `gdpr` (Article 33), `nis2` (Article 23), `dora` (Article 19), `cra` (Article 14), `hipaa` (Breach Notification Rule); operators select via `regime: \"gdpr\"` and `opts.deadlines` can override per-stage. Each stage records a tamper-evident audit event (`incident.report.stage_recorded`) with a `late: bool` + `lateBy: ms` flag — late filings get `outcome: \"late\"` so regulator audits can distinguish on-time from late-but-eventually filings. Operator-supplied `persist(record)` writes to a DB / SIEM / SOAR system; `onStage(event)` fires for synchronous routing. `status()` returns aggregate counts (open / closed / late-per-stage) for dashboards."
2950
+ }
2951
+ ]
2952
+ }
2953
+ ],
2954
+ "references": [
2955
+ {
2956
+ "label": "GDPR Article 33",
2957
+ "url": "https://gdpr-info.eu/art-33-gdpr/"
2958
+ },
2959
+ {
2960
+ "label": "NIS2 Directive Article 23",
2961
+ "url": "https://eur-lex.europa.eu/eli/dir/2022/2555/oj"
2962
+ },
2963
+ {
2964
+ "label": "DORA Article 19",
2965
+ "url": "https://eur-lex.europa.eu/eli/reg/2022/2554/oj"
2966
+ }
2967
+ ]
2968
+ },
2969
+ {
2970
+ "version": "0.8.8",
2971
+ "date": "2026-05-07",
2972
+ "headline": "`b.middleware.requireBoundKey` + audit-signing rotation + `b.circuitBreaker` + `b.htmlBalance.checkSafe` + FIPS boundary docs",
2973
+ "summary": "Ships three-axis bearer-API-key binding, operator-callable audit-signing rotation with historical re-sign, a top-level circuit-breaker re-export, an HTML balance + guard composite, a permissions predicate-shape warning, and FIPS 140-3 boundary documentation in SECURITY.md. ESLint pinned to 10.3.0 across CI workflows.",
2974
+ "sections": [
2975
+ {
2976
+ "heading": "Added",
2977
+ "items": [
2978
+ {
2979
+ "title": "`b.middleware.requireBoundKey`",
2980
+ "body": "Bearer-API-key middleware with three-axis binding: required scopes, bound-field equality (operator pulls values from headers / query / body via `getBoundField` getters; bound-fields registered on the key are checked with constant-time match), and peer-cert fingerprint allowlist (composes with v0.8.4's `b.crypto.hashCertFingerprint` / `isCertRevoked`). Operator-supplied async `resolver(apiKey)` returns the registered record or `null` when revoked. Refusals carry structured reasons (`no-bearer-token`, `key-unknown-or-revoked`, `missing-scope`, `bound-field-missing`, `bound-field-mismatch`, `peer-cert-required`, `peer-cert-not-pinned`); audit chain captures the keyId + reason on every refusal."
2981
+ },
2982
+ {
2983
+ "title": "`b.audit.rotateSigningKey` + `reSignAll(iter)`",
2984
+ "body": "Operator-callable rotation of the audit-signing keypair. Generates (or accepts BYO) a new keypair, copies the existing sealed file to a timestamped history path so historical signatures remain verifiable, re-seals with the operator's passphrase, and atomic-swaps the in-memory keys. Companion `reSignAll(iter)` walks an operator-supplied async iterable of `{ payload, signature, oldPublicKeyPem }` and re-signs each entry with the new key — the audit module's checkpoint store calls this to re-stamp historical checkpoints after a rotation."
2985
+ },
2986
+ {
2987
+ "title": "`b.circuitBreaker` top-level re-export",
2988
+ "body": "Top-level re-export of `b.retry.CircuitBreaker` so operators discover it alongside `b.retry`; same state machine, same `wrap()` API, ergonomic `create(opts)` factory."
2989
+ },
2990
+ {
2991
+ "title": "`b.htmlBalance.checkSafe(html, opts)`",
2992
+ "body": "Combines structural `balance()` check with a `b.guardHtml.gate` security pass under the same `{ profile, posture }` opt shape used by `b.fileUpload({ contentSafety })` / `b.staticServe({ contentSafety })`."
2993
+ }
2994
+ ]
2995
+ },
2996
+ {
2997
+ "heading": "Changed",
2998
+ "items": [
2999
+ {
3000
+ "title": "`b.permissions.policy(scope, predicate)` shape warning",
3001
+ "body": "Emits `permissions.policy_predicate_shape_warning` audit on register-time when the predicate's `.length < 2` (operators commonly forget the `context` argument and ship a predicate that's always-true on the actor parameter)."
3002
+ },
3003
+ {
3004
+ "title": "`SECURITY.md` FIPS 140-3 cryptographic boundary section",
3005
+ "body": "New section explaining the dual boundary — Node.js OpenSSL FIPS provider for classical primitives, vendored noble-* implementations for PQ algorithms. The latter implement FIPS-published algorithms but the implementations themselves are not CMVP-validated. Operator path for FIPS-mandated environments documented."
3006
+ },
3007
+ {
3008
+ "title": "ESLint pinned to 10.3.0 across CI workflows",
3009
+ "body": "Pinned across `ci.yml` / `npm-publish.yml` / `release-container.yml`. `eslint@latest` was silently letting new rule additions break the publish gate on releases that had passed the day before. The pin moves on operator-confirmed bumps."
3010
+ }
3011
+ ]
3012
+ }
3013
+ ]
3014
+ },
3015
+ {
3016
+ "version": "0.8.7",
3017
+ "date": "2026-05-06",
3018
+ "headline": "`b.auth.accessLock` three-mode access-lock primitive",
3019
+ "summary": "Ships an operator-intervention lock with `open` / `read-only` / `locked` modes for stop-the-world, idempotent-only, and full-deny operator windows during incident response, schema migrations, or break-glass review.",
3020
+ "sections": [
3021
+ {
3022
+ "heading": "Added",
3023
+ "items": [
3024
+ {
3025
+ "title": "`b.auth.accessLock`",
3026
+ "body": "Three modes: `\"open\"` is normal operation; `\"read-only\"` refuses non-idempotent methods (POST/PUT/PATCH/DELETE) with 503 + `Retry-After` while letting GET/HEAD/OPTIONS pass; `\"locked\"` refuses everything except an operator-supplied `passthroughPaths` allowlist (status / health / unlock endpoint). Operators flip modes via `lock.set(\"locked\", { actor, reason })`; the transition emits `auth.access_lock.mode_changed` audit + metric. `unlockRoles: [\"sre\", ...]` lets a privileged role bypass all three modes via `getRole(req)` so a break-glass operator can always reach the unlock endpoint. The boot-time mode emits `auth.access_lock.boot` so the audit chain captures the deploy posture."
3027
+ }
3028
+ ]
3029
+ }
3030
+ ]
3031
+ },
3032
+ {
3033
+ "version": "0.8.6",
3034
+ "date": "2026-05-06",
3035
+ "headline": "`b.middleware.dailyByteQuota` + `b.appShutdown` extensions + OIDC sub-claim cross-check",
3036
+ "summary": "Ships per-IP daily byte budgeting, lifecycle extensions (`onUncaught`, custom signal list, `pidLock`), `b.observability.otlpExporter` audit / metrics refinements, and a token-substitution defense on `b.auth.oauth.fetchUserInfo`.",
3037
+ "sections": [
3038
+ {
3039
+ "heading": "Added",
3040
+ "items": [
3041
+ {
3042
+ "title": "`b.middleware.dailyByteQuota`",
3043
+ "body": "Per-IP rolling 24-hour byte budget (24 hourly bins, slides per-second so a peer can't reset by waiting past midnight). Memory backend single-node by default; `opts.cache` wires `b.cache` for cluster-shared accounting. Refuses with 429 + `Retry-After` when peers exceed the quota; emits `middleware.daily_byte_quota.refused` audit + metric. Inbound + outbound bytes counted. Fail-open on cache backend errors with audited reason — a flaky cache no longer takes the framework down."
3044
+ },
3045
+ {
3046
+ "title": "`b.appShutdown` lifecycle extensions",
3047
+ "body": "`onUncaught` hook fires on `uncaughtException` / `unhandledRejection`; default is graceful-shutdown with `exitCode=1`. Operators can wire a hook for relay to PagerDuty / observability before exit. `opts.signals` now accepts a custom signal list (defaults to `[\"SIGTERM\",\"SIGINT\"]`); operators add `SIGUSR2` (nodemon restart), `SIGHUP` (terminal disconnect), `SIGQUIT` (`kill -3`) without subclassing. `b.appShutdown.pidLock(lockPath)` — single-instance file lock that writes `process.pid`, refuses to acquire when another live process holds the lock, reaps stale lock files (PID gone), and releases on shutdown."
3048
+ }
3049
+ ]
3050
+ },
3051
+ {
3052
+ "heading": "Changed",
3053
+ "items": [
3054
+ {
3055
+ "title": "`b.observability.otlpExporter` audit + metrics surface",
3056
+ "body": "New `system.observability.otlp_exporter.post_failed` audit emission distinguishes timeout / abort from generic network failure (operators can route timeout-rooted exporter degradation to a different alert channel). `stats()` now reports `droppedTotal` (queue overflow + export failed) and emits a `dropped_total` metric on every call so dashboards chart the running drop count."
3057
+ }
3058
+ ]
3059
+ },
3060
+ {
3061
+ "heading": "Fixed",
3062
+ "items": [
3063
+ {
3064
+ "title": "`b.auth.oauth.fetchUserInfo` OIDC sub-claim cross-check",
3065
+ "body": "Refuses on OIDC IdPs unless the caller threads `ufiOpts.idTokenSub` (the verified `sub` claim from `exchangeCode`'s `id_token`); cross-checks userinfo `sub === idTokenSub` to defend against token-substitution where a hostile IdP returns a different user's profile. Non-OIDC OAuth 2.0 deployments mis-flagged as `isOidc` opt out via `{ skipSubCheck: true }` with audited reason."
3066
+ }
3067
+ ]
3068
+ }
3069
+ ]
3070
+ },
3071
+ {
3072
+ "version": "0.8.5",
3073
+ "date": "2026-05-06",
3074
+ "headline": "Vendor-currency CI gate + `b.middleware.requireMtls`",
3075
+ "summary": "Adds a CI check that every vendored bundle in `lib/vendor/MANIFEST.json` matches the latest published version on npm (or the upstream default-branch HEAD for master-branch corpora), and ships `b.middleware.requireMtls` for soft mTLS enforcement composed with the v0.8.4 cert-fingerprint helpers.",
3076
+ "sections": [
3077
+ {
3078
+ "heading": "Added",
3079
+ "items": [
3080
+ {
3081
+ "title": "Vendor-currency CI gate",
3082
+ "body": "`scripts/check-vendor-currency.js` + a new CI job in `ci.yml` assert every npm-mapped vendored bundle in `lib/vendor/MANIFEST.json` matches the latest published version on the npm registry. Per-component check on meta-bundles (e.g. `peculiar-pki` → `@peculiar/x509` + `pkijs`). Master-branch corpus entries (`SecLists`) are checked against the GitHub Commits API for the bundled file's path on the source repo's default branch — if the upstream has commits newer than the manifest's `bundledAt` date, the gate fails. Registry errors stay advisory unless `BLAMEJS_VENDOR_CURRENCY_STRICT=1`. Operators run locally via `npm run check:vendor-currency`."
3083
+ },
3084
+ {
3085
+ "title": "`b.middleware.requireMtls`",
3086
+ "body": "New soft-enforcement middleware that rejects requests without an authenticated client certificate. Composes with `b.crypto.hashCertFingerprint` / `isCertRevoked` (added in v0.8.4) so operators pass `fingerprintAllowList: [...]` and `denyList: [...]` and the middleware does the constant-time match. `req.peerCert` + `req.peerFingerprint` are attached for downstream handlers. Audits `mtls.required.allowed` / `mtls.required.refused` with reason metadata."
3087
+ }
3088
+ ]
3089
+ }
3090
+ ]
3091
+ },
3092
+ {
3093
+ "version": "0.8.4",
3094
+ "date": "2026-05-06",
3095
+ "headline": "Supply-chain scanner findings + outbound HTTP posture + npm-publish unblock",
3096
+ "summary": "Routes the OTLP exporter through `b.httpClient` for PQC-hybrid + cert-pinning posture, lazy-loads `child_process` in `b.dev`, adds `responseMode` / `onRedirect` to `b.httpClient`, adds per-dial overrides to `b.wsClient`, ships new crypto + scheduler helpers, and corrects an SD-JWT default-alg test that blocked four prior publishes.",
3097
+ "sections": [
3098
+ {
3099
+ "heading": "Added",
3100
+ "items": [
3101
+ {
3102
+ "title": "`b.crypto.hashCertFingerprint(pem|der)` + `b.crypto.isCertRevoked(pemOrDer, denyList)`",
3103
+ "body": "Returns `{ hex, colon }` SHA3-512 digests and a constant-time deny-list match for cert-fingerprint pinning."
3104
+ },
3105
+ {
3106
+ "title": "`b.scheduler.register(name, intervalMs, fn)` + `b.scheduler.getStatus()`",
3107
+ "body": "Shorthand for the every-N-ms registration shape; `getStatus()` returns an aggregate health surface for probes / dashboards (started flag, isLeader, per-task list, totals)."
3108
+ }
3109
+ ]
3110
+ },
3111
+ {
3112
+ "heading": "Changed",
3113
+ "items": [
3114
+ {
3115
+ "title": "`b.observability.otlpExporter` default transport",
3116
+ "body": "No longer defaults to `globalThis.fetch`; the default transport is now `b.httpClient` (`node:https` through the framework's PQC-hybrid agent + cert-pinning + SSRF guard). The prior default leaked an outbound network surface that supply-chain scanners flagged. Operators on fetch-only edge runtimes still override via `opts.fetchImpl`."
3117
+ },
3118
+ {
3119
+ "title": "`b.dev.create()` lazy-requires `child_process` + refuses production by default",
3120
+ "body": "Lazy-requires `child_process` (was top-level — flagged on every install regardless of whether `b.dev` was used) and refuses to construct when `NODE_ENV=production` unless the operator passes `opts.allowProduction: true` with an audited reason. A misconfigured production deploy that accidentally wires the dev-mode restart loop now crashes loudly at boot rather than spawning shells on every save."
3121
+ },
3122
+ {
3123
+ "title": "`b.httpClient.request` adds `responseMode` + `onRedirect`",
3124
+ "body": "`responseMode: \"always-resolve\"` makes every response resolve with `{ statusCode, headers, body }` regardless of HTTP status. `onRedirect({ from, to, hop, headersStripped, statusCode, method })` lets operators throw to abort or rewrite the redirect chain."
3125
+ },
3126
+ {
3127
+ "title": "`b.wsClient.connect` per-dial overrides",
3128
+ "body": "Adds `urlFor(attempt)` and `tlsOptsFor(attempt)` for between-reconnect URL / TLS rotation; the new URL is re-validated through `ssrfGuard` so a hostile upstream can't redirect a reconnecting client at a private address. Post-`close()` `ECONNRESET` / `EPIPE` swallowed cleanly."
3129
+ },
3130
+ {
3131
+ "title": "`b.pqcAgent.create({ ecdhCurve })` accepts a caller-supplied stricter list",
3132
+ "body": "Operators can drop a group from the framework default but cannot widen with non-PQ groups (the prior hardcoded value blocked legitimate per-deployment narrowing)."
3133
+ }
3134
+ ]
3135
+ },
3136
+ {
3137
+ "heading": "Fixed",
3138
+ "items": [
3139
+ {
3140
+ "title": "npm-publish unblock: SD-JWT default-alg test",
3141
+ "body": "`test/layer-0-primitives/sd-jwt-vc.test.js` was asserting `DEFAULT_ALG === \"ES256\"` after v0.8.1 flipped the default to `ML-DSA-87`; the assertion now matches the lib (and `DEFAULT_HASH_ALG` for `sha3-512`). v0.8.1 / v0.8.2 / v0.8.3 all failed the npm-publish gate on this single test."
3142
+ }
3143
+ ]
3144
+ }
3145
+ ]
3146
+ },
3147
+ {
3148
+ "version": "0.8.3",
3149
+ "date": "2026-05-06",
3150
+ "headline": "Release-gate fixes + post-v0.8.2 hardening",
3151
+ "summary": "Closes the wiki opts-drift gate for the v0.8.1 `requireOrigin` opt, a gitleaks false-positive on KEM-envelope shapes, and ships two functional additions on `b.httpClient` and `b.wsClient`.",
3152
+ "sections": [
3153
+ {
3154
+ "heading": "Added",
3155
+ "items": [
3156
+ {
3157
+ "title": "`b.httpClient.request({ responseMode: \"always-resolve\" })`",
3158
+ "body": "Every request resolves with `{ statusCode, headers, body }` regardless of HTTP status — operators using the framework as an inbound-proxy upstream no longer have to wrap each call in a try/catch to recover the body of a 4xx/5xx."
3159
+ }
3160
+ ]
3161
+ },
3162
+ {
3163
+ "heading": "Fixed",
3164
+ "items": [
3165
+ {
3166
+ "title": "Wiki primitive-section opts-key drift on `b.middleware.csrfProtect`",
3167
+ "body": "The `requireOrigin` opt added in v0.8.1 was not documented in the wiki seeder; now listed alongside `checkOrigin` / `allowedOrigins`."
3168
+ },
3169
+ {
3170
+ "title": "gitleaks secret-scan false positives on KEM-envelope shapes",
3171
+ "body": "Findings against `{ privateKey, cipherText }` parameter-name shapes in `lib/crypto.js` error messages and the v0.8.0 CHANGELOG entry are now allowlisted via `.gitleaks.toml` (parameter-name shape allowlist + pinned commit fingerprints for the v0.8.0 entry)."
3172
+ },
3173
+ {
3174
+ "title": "`b.wsClient` post-close error noise",
3175
+ "body": "Swallows post-`close()` `ECONNRESET` / `EPIPE` errors so a clean shutdown doesn't surface a noisy unhandled-error event when the kernel races the FIN with an in-flight write."
3176
+ },
3177
+ {
3178
+ "title": "`SECURITY.md` documents the `allowInternal: true` test-pattern",
3179
+ "body": "Legitimate same-host integration tests opt in explicitly with audited reason — never as a production default."
3180
+ }
3181
+ ]
3182
+ }
3183
+ ]
3184
+ },
3185
+ {
3186
+ "version": "0.8.2",
3187
+ "date": "2026-05-06",
3188
+ "headline": "ESLint fixes for the v0.8.1 npm-publish gate",
3189
+ "summary": "Two no-op lint fixes that unblock the publish workflow; functional behaviour unchanged from v0.8.1.",
3190
+ "sections": [
3191
+ {
3192
+ "heading": "Fixed",
3193
+ "items": [
3194
+ {
3195
+ "title": "`lib/guard-csv.js` bidi-prefix regex",
3196
+ "body": "Now uses explicit `\\uXXXX` escapes (was tripping `no-irregular-whitespace` + `no-misleading-character-class` on the literal-codepoint form)."
3197
+ },
3198
+ {
3199
+ "title": "`lib/redact.js` URL-bearer-query detector",
3200
+ "body": "Drops a redundant `\\-` escape inside a character class flagged by ESLint."
3201
+ }
3202
+ ]
3203
+ }
3204
+ ]
3205
+ },
3206
+ {
3207
+ "version": "0.8.1",
3208
+ "date": "2026-05-06",
3209
+ "headline": "Hardening sweep across audit emission, crypto defaults, auth bypass closure, storage, HTTP, and observability",
3210
+ "summary": "Defense-in-depth fixes across audit emission canonicalisation, PQC-first crypto defaults, auth bypass closure (break-glass, mfaWindowMs, fingerprint-drift, bearer 401 shape, jti minting, OIDC nonce), storage SQLi closures, HTTP and outbound transport posture, content-safety bypasses, and redaction coverage. No new operator-facing primitives.",
3211
+ "sections": [
3212
+ {
3213
+ "heading": "Changed",
3214
+ "items": [
3215
+ {
3216
+ "title": "Audit emission canonicalisation",
3217
+ "body": "`audit.safeEmit` now normalises non-canonical outcomes (`ok` / `fail` / `warning` / `duplicate` / `skip` → `success` / `failure`) and replaces hyphens in action-name segments with underscores. The strict regex enforced by `audit.record` was silently dropping events from `b.flag` / `b.outbox` / `b.inbox` / `b.session` (idle / absolute / fingerprint-drift) / `b.db` (integrity-check) / `b.compliance.aiAct` / `b.config-drift` / `b.log-stream` / `b.pubsub` (and fixes a positional-signature bug at the publish call site). Chain-write integrity failures now emit `system.audit.chain_write_dropped` to observability so operators alerting on rate-drop still see a signal when audit itself is the broken sink."
3218
+ },
3219
+ {
3220
+ "title": "PQC-first crypto defaults tightened",
3221
+ "body": "`b.mtlsCa` `caKeySealedMode` default flipped from `\"auto\"` to `\"required\"`; legacy `\"auto\"` (load whichever exists, fall back to plaintext) is removed. Operators opt back to plaintext explicitly via `\"disabled\"` with audited reason. `b.network.tls` default key-share preference list now leads with `SecP384r1MLKEM1024` (highest-PQ hybrid registered in `TLS_GROUP_PREFERENCE`) and drops `secp256r1`. `b.auth.sdJwtVc` defaults to `ML-DSA-87` + `sha3-512` (was `ES256` + `sha-256`). `b.crypto.encrypt` emits `system.crypto.hybrid_disabled` audit when called with only an ML-KEM public key. `b.auth.totp` emits `auth.totp.algorithm_downgraded` audit on every SHA-256 enrolment / verification."
3222
+ }
3223
+ ]
3224
+ },
3225
+ {
3226
+ "heading": "Fixed",
3227
+ "items": [
3228
+ {
3229
+ "title": "Auth bypass closures across break-glass, MFA, sessions, bearer, OIDC nonce",
3230
+ "body": "`b.breakGlass` `policy.requireScope` is now enforced at `grant()` time (was accepted, persisted, and surfaced via `policyGet` but never consulted). `b.permissions` `requireMfa: true` defaults to a 15-minute `mfaWindowMs` floor when neither route nor role supplies one. `b.middleware.attachUser` threads `req` through `session.verify` so the documented fingerprint-drift / IP-UA pin / anomaly-score defenses fire on the standard middleware path. `b.middleware.bearerAuth` returns 401 with `WWW-Authenticate: Bearer error=\"invalid_request\"` when an `Authorization` header is present but doesn't parse against the configured scheme (was falling through to cookie-session); `realm` is CRLF-validated at create-time. `b.bearerAuth` sets `req._bearerAuthHandled` after success so `b.middleware.attachUser` skips re-reading the same header. `b.breakGlass.unsealRow` SELECTs the target row before incrementing `rowsConsumed` so a typo'd row id no longer exhausts a `maxRowsPerGrant: 1` grant. `b.auth.password` HIBP path fail-closes when more than half the response lines are unparseable (poisoned-mirror defense). `b.auth.jwt.sign` auto-mints a `jti` when `expiresInSec` is set and the operator didn't supply one. `b.auth.oauth.exchangeCode` requires `nonce` on OIDC flows when `authorizationUrl()` produced one (explicit `skipNonceCheck: true` for legacy IdPs). `b.auth.lockout` cache-error signal now also rides the audit chain. `b.middleware.csrfProtect` cookie regex tightened from `{2,}` to `{64}` hex chars (matches `forms.generateCsrfToken` output) so a sibling-subdomain XSS can't plant `csrf=ab` and submit matching `X-CSRF-Token`; new `requireOrigin: true` opt for browser-only routes; `csrf.bad_cookie_value` audit on planted-short-cookie refusals."
3231
+ },
3232
+ {
3233
+ "title": "Storage / SQLi closures",
3234
+ "body": "`b.retention` calls `safeSql.validateIdentifier` on every operator-supplied table name, age field, soft-delete field, legal-hold field, and cascade FK before reaching SQL string concatenation. `b.db` at-rest `db.enc` envelope binds `(data dir, node identity)` AAD so two deployments sharing the operator passphrase can't swap `db.enc` files; old envelopes still decrypt via a one-release backwards-compat fallback. `b.externalDb` `SET LOCAL` GUC values capped at 4 KiB. `b.cache` set path swapped from `JSON.stringify` to `safeJson.stringify` (refuses Buffer / circular / Date round-trip ambiguity). `b.objectStore.setObjectRetention` does a `getObjectRetention` pre-check and refuses client-side when existing retention mode is `COMPLIANCE` and the caller tries to shorten it or pass `bypassGovernance: true`. `b.inbox` rejects NUL + C0 controls in `messageId` / `source` (closes the dedupe-collision attack on truncating drivers)."
3235
+ },
3236
+ {
3237
+ "title": "HTTP / network posture",
3238
+ "body": "`b.wsClient.connect` wires `b.ssrfGuard` symmetric to `b.httpClient` (cloud-metadata / private / loopback / link-local / reserved IPs hard-denied; `allowInternal: true` opts in). `b.wsClient` outbound `tls.connect` pins `minVersion: \"TLSv1.3\"`. `b.app` HTTP/2 server pins `maxOutstandingPings: 10` (CVE-2019-9512 ping-flood class). `b.middleware.cors` always appends `Vary: Origin` when the request carried an Origin. `b.staticServe` adds `maxRangeBytes` cap (default 64 MiB) — refuses single-range requests larger than the cap with 416 (slowloris-range defense). `b.middleware.bodyParser` per-part header bytes now count toward `totalSize` (closes a 120 × 16 KiB amplification surface). `b.ssrfGuard` `_ipv4ToInt` strict octet validation refuses non-numeric segments instead of silently coercing to 0. `b.cluster` heartbeat picks up ±20% per-tick jitter on followers; `MIN_LEASE_TTL` bumped from 5s to 10s."
3239
+ },
3240
+ {
3241
+ "title": "Content-safety bypasses + observability redaction",
3242
+ "body": "`b.guardHtml._extractScheme` + `b.guardSvg._extractScheme` decode HTML5 named entities (`&Tab;` / `&NewLine;` / `&colon;` / `&sol;` / etc.) before scheme-allowlist matching (closes the `java&Tab;script:` bypass class). `b.guardCsv` strips ZWSP / RTLO / LRM / RLM / BOM at cell-start before the formula-prefix scan. `b.guardAll._verifyParity` walks `STANDALONE_GUARDS` too (filename / domain / uuid / cidr / time / mime / jwt / oauth / graphql / shell / regex / jsonpath / template / image / pdf / auth). `b.fileUpload._checkAllowedFileType` cross-checks operator-supplied claimed MIME against magic-byte detection and refuses on family mismatch. `b.mail` attachment validation wires `b.guardFilename.validate({ profile: \"strict\" })` + magic-byte / claimed-MIME cross-check. `b.redact` SENSITIVE_FIELDS now covers `x-api-key` / `x-apikey` / `x_api_key` / `api-key` plus DPoP / OAuth 2.1 fields (`jwk`, `dpop`, `proof`, `assertion`, `client_assertion`, `id_token_hint`, `code_verifier`, `client_secret`, `refresh_token`, `access_token`); new value-shape detector redacts query-string `?token=` / `?access_token=` / `?api_key=` patterns inside URL fields. `b.logStream` syslog sink strips CR / LF from MSG content per RFC 5424 §6.4. `b.compliance.set` rejection emits `compliance.posture.set_rejected` audit on `unknown-posture` and `already-set` paths."
3243
+ },
3244
+ {
3245
+ "title": "Supply chain hygiene",
3246
+ "body": "`scripts/vendor-update.sh` auto-runs `scripts/refresh-vendor-manifest.js` so `MANIFEST.json` sha256 hashes track the on-disk bundle without a separate operator step."
3247
+ }
3248
+ ]
3249
+ },
3250
+ {
3251
+ "heading": "Detectors",
3252
+ "items": [
3253
+ {
3254
+ "title": "`audit-action-with-hyphen` + `non-canonical-audit-outcome`",
3255
+ "body": "Two new `codebase-patterns` detectors catch new sites at gate time that emit hyphenated audit-action segments or non-canonical outcome strings."
3256
+ }
3257
+ ]
3258
+ }
3259
+ ]
3260
+ },
3261
+ {
3262
+ "version": "0.8.0",
3263
+ "date": "2026-05-06",
3264
+ "headline": "ARC chain construction + transactional inbox + API spec parsers + wsClient hardening",
3265
+ "summary": "Minor bump landing relay-side ARC signing, transactional dedupe-on-receive inbox, OpenAPI/AsyncAPI parsers, a named ML-KEM-768 + X25519 decrypt helper, and substantial outbound WebSocket-client hardening (decompression-bomb cap, UTF-8 fatal validation, control-frame caps, permanent-error classifier, audit-metadata enrichment).",
3266
+ "sections": [
3267
+ {
3268
+ "heading": "Added",
3269
+ "items": [
3270
+ {
3271
+ "title": "`b.mail.arc.sign` relay-side RFC 8617 ARC chain construction",
3272
+ "body": "Companion to the existing `b.mail.arc.verify`. Produces AAR + AMS + AS headers, prepends them in RFC-recommended order, enforces cv= rules (`i=1` requires `cv=none`, `i>=2` requires `cv=pass` / `cv=fail`), and emits chain-gap detection (`i=N` requires N-1 prior hops). Supports rsa-sha256 and ed25519-sha256; CRLF-injection refused on operator-supplied authResults. Emits `dkim.arc.signed` audit on success."
3273
+ },
3274
+ {
3275
+ "title": "`b.inbox.create` transactional dedupe-on-receive",
3276
+ "body": "Companion to `b.outbox`. Guarantees exactly-once handling by recording every `(source, messageId)` pair in the same transaction as the business state change — duplicate delivery short-circuits via the `(source, message_id)` PRIMARY KEY. High-level `handle(opts, fn)` wraps `externalDb.transaction`; low-level `recordReceive` / `markProcessed` for operators managing transactions directly. Includes `declareSchema` / `sweep(retentionDays)` / `getStats` / `isFresh`; postgres + sqlite dialect support; audit emissions `inbox.received` / `handled` / `handle_failed` / `swept`."
3277
+ },
3278
+ {
3279
+ "title": "`b.openapi.parse` + `b.asyncapi.parse`",
3280
+ "body": "Validate external specs (only the build path existed previously). Returns `{ doc, errors[], valid }` covering version, info, paths / channels / operations, response.description, path-parameter `required:true`, and dangling security references."
3281
+ },
3282
+ {
3283
+ "title": "`b.crypto.decryptMlkem768X25519` named decrypt helper",
3284
+ "body": "Symmetric counterpart to the existing `encryptMlkem768X25519`. Rejects ciphertexts under any other KEM id at the head with a clear error rather than falling through the generic dispatch path."
3285
+ },
3286
+ {
3287
+ "title": "`b.wsClient.cancelReconnect()` operator API",
3288
+ "body": "Stops in-flight reconnect timers. `close()` mid-reconnect now also cancels the pending timer rather than returning early on the closed state."
3289
+ }
3290
+ ]
3291
+ },
3292
+ {
3293
+ "heading": "Changed",
3294
+ "items": [
3295
+ {
3296
+ "title": "`b.wsClient` hardening + extensions",
3297
+ "body": "Adds `handshakeGuid` opt mirroring the server (operators with non-RFC-6455 GUIDs). Decompression-bomb defense via `zlib.inflateRawSync` `maxOutputLength` cap closes the small-frame-to-GB expansion. Fatal UTF-8 validation on text frames + close-frame reasons (RFC 6455 §5.6). Control-frame ≤125-byte cap + FIN=1 enforcement (RFC 6455 §5.5). RSV1-on-continuation rejection (RFC 7692 §6.1). Permanent-error classifier so 4xx handshake responses / accept-mismatch / bad-subprotocol / bad-upgrade / bad-status-line / message-too-big skip reconnect (no auth-failure hammering). `close(code, reason)` truncates >123-byte UTF-8 reasons at codepoint boundaries. `permessage-deflate` `server_max_window_bits` parsing with [8, 15] range enforcement. Audit metadata enriched with `bytesSent` / `bytesReceived` / `attempt` / `peerCertFingerprint` / `serverWindowBits` / `tls` / `permanent`. CRLF validation on the operator-supplied `Origin:` header matches the existing custom-header validation."
3298
+ }
3299
+ ]
3300
+ }
3301
+ ],
3302
+ "references": [
3303
+ {
3304
+ "label": "RFC 8617 ARC",
3305
+ "url": "https://www.rfc-editor.org/rfc/rfc8617.html"
3306
+ },
3307
+ {
3308
+ "label": "RFC 6455 WebSocket",
3309
+ "url": "https://www.rfc-editor.org/rfc/rfc6455.html"
3310
+ },
3311
+ {
3312
+ "label": "RFC 7692 WebSocket permessage-deflate",
3313
+ "url": "https://www.rfc-editor.org/rfc/rfc7692.html"
3314
+ }
3315
+ ]
3316
+ }
3317
+ ]
3318
+ }