@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,3811 @@
1
+ {
2
+ "$schema": "../scripts/release-notes-consolidated-schema.json",
3
+ "minor": "0.7",
4
+ "releases": [
5
+ {
6
+ "version": "0.7.114",
7
+ "date": "2026-05-06",
8
+ "headline": "`b.vault.aad` AAD-bound sealed columns + `b.wsClient` outbound WebSocket client",
9
+ "summary": ". `b.vault.aad.seal(plaintext, aadParts)` / `b.vault.aad.unseal(value, aadParts)` binds the seal to an AAD tuple `(table, rowId, column, schemaVersion)` so the AEAD tag fails on any decrypt where the AAD differs — copy-paste between rows, replay across schema-version bumps, and table-mismatch attacks all surface as a refused decrypt.",
10
+ "sections": [
11
+ {
12
+ "heading": "Added",
13
+ "items": [
14
+ {
15
+ "title": "`b.vault.aad.seal(plaintext, aadParts)`",
16
+ "body": "/. (See CHANGELOG for full context)."
17
+ },
18
+ {
19
+ "title": "`b.vault.aad.unseal(value, aadParts)`",
20
+ "body": "binds the seal to an AAD tuple `(table, rowId, column, schemaVersion)` so the AEAD tag fails on any decrypt where the AAD differs — copy-paste between rows, replay across schema-version bumps, and table-mismatch attacks all surface as a refused decrypt. Symmetric key derived per-row via SHAKE256 over `(\"vault.aad/v1/\" || vault-root || canonical-AAD)` with the AAD threaded into XChaCha20-Poly1305's tag. `buildColumnAad` / `buildContextAad` helpers produce canonical (sorted-keys, length-prefixed) AAD bytes; `reseal(value, fromAad, toAad)` re-binds a value to a new context after authenticating the source. Audit emissions: `vault.aad.sealed` / `vault.aad.unseal_failed`."
21
+ },
22
+ {
23
+ "title": "`b.wsClient.connect(url, opts)`",
24
+ "body": "ships the outbound RFC 6455 WebSocket client — companion to `b.websocket` (server-side). HTTP/1.1 Upgrade with Sec-WebSocket-Key generation + Sec-WebSocket-Accept verification (rejects on hash mismatch); subprotocol + permessage-deflate (RFC 7692) negotiation; client-side frame masking (RFC 6455 §5.3); TLS via `b.network.tls.pqc` (X25519MLKEM768 hybrid handshake, security-defaults-on); heartbeat ping/pong with pongDeadline tracking; auto-reconnect with exponential-backoff + full jitter; CRLF-injection defense on operator-supplied headers; configurable `maxMessageBytes` / `maxFrameBytes` / `pingMs` / `pongMs` / `handshakeTimeoutMs` / `reconnect: { maxAttempts, baseMs, maxMs }`. EventEmitter API: `open` / `message` / `close` / `error` / `reconnecting`. Reuses `FrameParser` and `serializeFrame` from `lib/websocket.js` so the wire layer is identical to the server. Integration test `test/integration/ws-client-roundtrip.test.js` boots a real `http.Server` driven by `b.websocket` primitives and dials it with `b.wsClient`, exercising plain ws:// handshake + subprotocol negotiation + text/binary echo + ping/pong heartbeat + close round-trip + permessage-deflate compress/inflate end-to-end. Audit emissions: `wsclient.connected` / `wsclient.closed` / `wsclient.error`."
25
+ }
26
+ ]
27
+ }
28
+ ],
29
+ "references": [
30
+ {
31
+ "label": "RFC 6455",
32
+ "url": "https://www.rfc-editor.org/rfc/rfc6455.html"
33
+ },
34
+ {
35
+ "label": "RFC 7692",
36
+ "url": "https://www.rfc-editor.org/rfc/rfc7692.html"
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "version": "0.7.113",
42
+ "date": "2026-05-06",
43
+ "headline": "`@noble/post-quantum` attribution sweep",
44
+ "summary": "NOTICE picks up the v0.6.1 vendored bundle with full copyright + Used-for + thank-you line per the framework's attribution convention. README's \"Vendored dependencies\" dependency table picks up the noble-post-quantum row alongside noble-ciphers. The wiki home page's \"Special thanks\" list cites Paul Miller for both noble-ciphers and noble-post-quantum.",
45
+ "sections": [
46
+ {
47
+ "heading": "Added",
48
+ "items": [
49
+ {
50
+ "title": "`@noble/post-quantum` attribution sweep",
51
+ "body": "`@noble/post-quantum` attribution sweep. NOTICE picks up the v0.6.1 vendored bundle with full copyright + Used-for + thank-you line per the framework's attribution convention. README's \"Vendored dependencies\" dependency table picks up the noble-post-quantum row alongside noble-ciphers. The wiki home page's \"Special thanks\" list cites Paul Miller for both noble-ciphers and noble-post-quantum. The wiki `welcome` page's \"What's in the box\" table picks up `@noble/post-quantum` ↔ `b.pqcSoftware` with the security-first defaults (ML-KEM-1024, ML-DSA-87, SLH-DSA-SHAKE-256f). Pure docs / attribution sweep — no behaviour changes."
52
+ }
53
+ ]
54
+ }
55
+ ]
56
+ },
57
+ {
58
+ "version": "0.7.112",
59
+ "date": "2026-05-06",
60
+ "headline": "`b.asyncapi` AsyncAPI 3.0 schema-document builder + `b.pqcSoftware` pure-JS PQC primitive wrapper",
61
+ "summary": "around vendored `@noble/post-quantum`. `b.asyncapi.create({ info, servers, defaultContentType, security, externalDocs, tags, id })` returns an event-driven sibling to `b.openapi` — operators describe pubsub / websocket / kafka / mqtt / amqp surfaces as a single document the framework can serve at `/asyncapi.json` (or YAML).",
62
+ "sections": [
63
+ {
64
+ "heading": "Added",
65
+ "items": [
66
+ {
67
+ "title": "`b.asyncapi.create({ info, servers, defaultContentType, security, externalDocs, tags, id })`",
68
+ "body": "returns an event-driven sibling to `b.openapi` — operators describe pubsub / websocket / kafka / mqtt / amqp surfaces as a single document the framework can serve at `/asyncapi.json` (or YAML)."
69
+ },
70
+ {
71
+ "title": "`builder.channel(channelId, opts)`",
72
+ "body": "registers channels with messages / parameters / per-channel bindings;."
73
+ },
74
+ {
75
+ "title": "`builder.operation(operationId, opts)`",
76
+ "body": "registers `send` / `receive` operations against declared channels (dangling-channel references throw at registration). `builder.schema / message / parameter / correlationId / requestBody` register reusable components."
77
+ },
78
+ {
79
+ "title": "`builder.security.add / require`",
80
+ "body": "with same scheme builders shared with `b.openapi.security` (bearer / basic / apiKey / oauth2 / openIdConnect / mtls / dpop)."
81
+ },
82
+ {
83
+ "title": "`builder.toJson() / toJsonString() / toYaml()`",
84
+ "body": "final dangling-security-reference checks; YAML emitter shared with `b.openapi`."
85
+ },
86
+ {
87
+ "title": "`b.asyncapi.bindings.{websockets,kafka,amqp,mqtt,http}`",
88
+ "body": "typed binding builders for the four protocols framework primitives speak; rejection on bad shape (websockets method ∈ {GET, POST}, kafka partitions/replicas > 0, amqp `is` ∈ {queue, routingKey}, mqtt qos ∈ {0, 1, 2})."
89
+ },
90
+ {
91
+ "title": "`b.asyncapi.traits.{operation, message, applyOperation, applyMessage}`",
92
+ "body": "AsyncAPI trait composition with shallow-merge semantics — operators define a trait once (e.g. \"every kafka publish carries tracing-header envelope\") and apply it across operations / messages."
93
+ },
94
+ {
95
+ "title": "`b.middleware.asyncapiServe({ document, pathJson, pathYaml, accessControl, cacheControl,",
96
+ "body": "mirrors `openapiServe`: GET / HEAD only, SHA3-512 ETag for conditional 304s, CORS gating."
97
+ },
98
+ {
99
+ "title": "`b.pqcSoftware`",
100
+ "body": "ships pure-JS FIPS 203 / 204 / 205 PQC algorithms via `lib/vendor/noble-post-quantum.cjs` (Paul Miller's `@noble/post-quantum` v0.6.1, MIT, vendored under `lib/vendor/MANIFEST.json` with SHA-256 pin). Usable server-side and client-side; ciphertexts FIPS 203 conformant in both directions with Node's built-in WebCrypto ML-KEM. Defaults pin to the highest cat-5 level: `DEFAULT_KEM` = ML-KEM-1024, `DEFAULT_LATTICE_SIG` = ML-DSA-87, `DEFAULT_HASH_SIG` = SLH-DSA-SHAKE-256f. Surface: `ml_kem_512` / `ml_kem_768` / `ml_kem_1024` (FIPS 203 KEM), `ml_dsa_44` / `ml_dsa_65` / `ml_dsa_87` (FIPS 204 lattice signatures), `slh_dsa_sha2_*f` / `slh_dsa_shake_*f` (FIPS 205 hash signatures), `isAvailable()` + `listAlgorithms()` + getter-style algorithm accessors. Audit emissions: `asyncapi.document.built` / `asyncapi.document.served`. 200+ test cases."
101
+ }
102
+ ]
103
+ }
104
+ ]
105
+ },
106
+ {
107
+ "version": "0.7.111",
108
+ "date": "2026-05-06",
109
+ "headline": "`b.flag` + `b.middleware.flagContext` — OpenFeature-aligned feature-flag client",
110
+ "summary": "`b.flag.create({ provider, providers, defaultEvaluationContext, audit, errorHandler, hooks })` returns a flag client with `getValue / getBoolean / getString / getNumber / getObject / getDetails / getValues / getDetailsAll / addProvider / removeProvider / list / middleware`.",
111
+ "sections": [
112
+ {
113
+ "heading": "Added",
114
+ "items": [
115
+ {
116
+ "title": "`b.flag.create({ provider, providers, defaultEvaluationContext, audit, errorHandler, hooks })`",
117
+ "body": "returns a flag client with `getValue / getBoolean / getString / getNumber / getObject / getDetails / getValues / getDetailsAll / addProvider / removeProvider / list / middleware`. Hot-path drop-silent — operator-supplied defaults returned on flag-not-found / provider-error; an `errorHandler` callback + audit emission on `flag.evaluation.error` surface the issue without taking down the request."
118
+ },
119
+ {
120
+ "title": "`b.flag.providers`",
121
+ "body": "ships three first-party providers: `memory({ flags })` for in-process / test flag sets, `localFile({ path, watch })` for JSON-file-backed flags with optional watch-on-change reload (hot-path-tolerant — bad JSON during reload is drop-silent), and `environmentVariable({ prefix, flags })` for `FLAG_*` env-var overrides. Flag specs validate at registration time per the no-MVP rule: `default` must reference a registered variant, every rule's `variant` must reference a registered variant, every rollout entry's percentage must be in [0, 100] with the sum bounded at 100."
122
+ },
123
+ {
124
+ "title": "`b.flag.targeting`",
125
+ "body": "evaluates rules with 14 operators (eq / neq / in / nin / gt / gte / lt / lte / starts_with / ends_with / contains / regex / exists / not_exists / between); regex patterns capped at 200 chars (DoS defense); rules support nested-attribute paths (`user.profile.tier`); conjunction across `conditions[]`."
126
+ },
127
+ {
128
+ "title": "`b.flag.context`",
129
+ "body": "ships evaluation-context builders: `create`, `merge`, `fromRequest({ userKey, extra })` (extracts targeting key from `req.user.id` or anonymous fallback hashed from clientIp + userAgent), `bucketOf(targetingKey, flagKey)` deterministic SHA3-512 percentage bucket helper."
130
+ },
131
+ {
132
+ "title": "`b.flag.cache(downstream, { ttlMs, maxEntries })`",
133
+ "body": "wraps a downstream provider with a per-`(targetingKey, flagKey)` TTL cache backed by an insertion-ordered Map (oldest-first eviction at maxEntries cap; flag-not-found never cached so operators can add flags later); `cached.bust()` clears all entries; `cached.stats()` returns `{ size, hits, misses, evictions, hitRatio, ttlMs, maxEntries }`."
134
+ },
135
+ {
136
+ "title": "`b.middleware.flagContext({ userKey, userKeyHeader, extractAttributes, tenantKeyHeader })`",
137
+ "body": "request-time middleware that extracts an evaluation context onto `req.flagCtx` for downstream handlers and multi-client flag setups (separate from the per-client `flag.middleware()` accessor that attaches `req.flag.{getBoolean,getString,...}` directly)."
138
+ },
139
+ {
140
+ "title": "OpenFeature hooks",
141
+ "body": "`before / after / error / finally` callbacks fire around every evaluation; throwing hooks are drop-silent (observability, not blocking). Audit emissions: `flag.evaluated` / `flag.evaluation.error` / `flag.cache.bust`. 130+ test cases."
142
+ }
143
+ ]
144
+ }
145
+ ]
146
+ },
147
+ {
148
+ "version": "0.7.110",
149
+ "date": "2026-05-06",
150
+ "headline": "`b.openapi` + `b.middleware.openapiServe` — OpenAPI 3.1 schema-document builder",
151
+ "summary": ". `b.openapi.create({ info, servers, externalDocs, tags, security })` returns an immutable-on-`toJson` builder for hand-authored API contracts.",
152
+ "sections": [
153
+ {
154
+ "heading": "Added",
155
+ "items": [
156
+ {
157
+ "title": "`b.openapi.create({ info, servers, externalDocs, tags, security })`",
158
+ "body": "returns an immutable-on-`toJson` builder for hand-authored API contracts."
159
+ },
160
+ {
161
+ "title": "`builder.path(method, urlPattern, opts)`",
162
+ "body": "registers operations with full RFC-9110-method validation (get / put / post / delete / options / head / patch / trace), path-template `{name}` placeholders cross-checked against declared `parameters: [{ in: \"path\", required: true }]` (build-time throw on mismatch), required `responses` map per OpenAPI 3.1 §4.8.5 with required `description` on every response, body-content map under `requestBody.content[mediaType].schema`."
163
+ },
164
+ {
165
+ "title": "`builder.schema(name, schemaSpec)`",
166
+ "body": "/ `response(...)` / `parameter(...)` / `requestBody(...)` / `header(...)` / `example(...)` register reusable `components`. Schema specs accept three forms: a `b.safeSchema` object (walked into JSON Schema 2020-12 by `lib/openapi-schema-walk.js`), a hand-shaped JSON Schema object (passes through with shape validation), or a primitive type-name string."
167
+ },
168
+ {
169
+ "title": "`builder.security.add(name, scheme)`",
170
+ "body": "+ `security.require(requirement)` register security schemes; the builder surfaces `b.openapi.security.{bearer,basic,apiKey,oauth2,openIdConnect,mtls,dpop}` typed builders that validate every IANA-registered scheme shape (apiKey `in` ∈ {header, query, cookie}; oauth2 `flows` with per-flow `authorizationUrl` / `tokenUrl` / `scopes` validation; mTLS uses the OpenAPI 3.1 `mutualTLS` type)."
171
+ },
172
+ {
173
+ "title": "`builder.toJson()`",
174
+ "body": "runs final dangling-reference checks (every `security` requirement key MUST resolve to a registered scheme). `builder.toJsonString(indent)` +."
175
+ },
176
+ {
177
+ "title": "`builder.toYaml()`",
178
+ "body": "emit the document; the YAML emitter (`lib/openapi-yaml.js`) handles spec-quoted strings (numbers / booleans / dates / yaml-special tokens), nested arrays / objects, empty `[]` / `{}`. `builder.middleware({ pretty, cacheControl })` is the single-builder embed;."
179
+ },
180
+ {
181
+ "title": "`b.middleware.openapiServe({ document, pathJson, pathYaml, accessControl, cacheControl, pretty,",
182
+ "body": "is the framework-style mounted middleware that responds at `/openapi.json` + `/openapi.yaml` (configurable), emits SHA3-512-derived ETag for conditional-GET 304s, gates by `accessControl: \"public\" | \"same-origin\"` for the `Access-Control-Allow-Origin: *` header, falls through on non-GET / non-HEAD methods. Audit emissions: `openapi.document.built` / `openapi.document.served`. 100+ test cases."
183
+ }
184
+ ]
185
+ }
186
+ ]
187
+ },
188
+ {
189
+ "version": "0.7.109",
190
+ "date": "2026-05-06",
191
+ "headline": "`b.compliance.aiAct` + `b.middleware.aiActDisclosure` — full EU AI Act (Regulation (EU) 2024/1689)",
192
+ "summary": "compliance primitive with deadline-aware classification, Article 5 prohibited-practices catalog, Article 6 + Annex III high-risk classifier, Article 12 logging helpers (with biometric-system-specific minimum-fields enforcement per Art.",
193
+ "sections": [
194
+ {
195
+ "heading": "Added",
196
+ "items": [
197
+ {
198
+ "title": "`b.compliance.aiAct.classify({ purpose, deployContext, deployerType, ... })`",
199
+ "body": "returns `{ tier, prohibitedHits, annexIIIHits, obligations, action, legalReference }` — tier is `prohibited` / `high-risk` / `limited-risk` / `general-purpose` / `minimal-risk`."
200
+ },
201
+ {
202
+ "title": "`b.compliance.aiAct.prohibited`",
203
+ "body": "ships the eight Art. 5 prohibited practices catalog (subliminal manipulation, vulnerable-group exploitation, social scoring, profiling-only predictive policing, untargeted facial scraping, workplace/education emotion inference, sensitive-attribute biometric categorisation, real-time RBI in public spaces) with conservative classifier and exemption handling."
204
+ },
205
+ {
206
+ "title": "`b.compliance.aiAct.risk`",
207
+ "body": "ships the eight Annex III rows with per-row Article 9-15 obligation lists (Article 27 FRIA added for law-enforcement / migration)."
208
+ },
209
+ {
210
+ "title": "`b.compliance.aiAct.transparency`",
211
+ "body": "ships banner / htmlBanner / watermark / jsonLdDisclosure / metaTags builders for the four Art. 50 obligations."
212
+ },
213
+ {
214
+ "title": "`b.compliance.aiAct.logging`",
215
+ "body": "ships buildEvent / emit / logEvent / loggerFor / retentionFloorMs (per Art. 19 — 180-day default, 365-day floor for financial / employment / law-enforcement). Biometric systems require the Art. 12(3) minimum fields (periodStart, periodEnd, referenceDatabase, matchedInputRef, verifiers); the builder throws when they're missing."
216
+ },
217
+ {
218
+ "title": "`b.compliance.aiAct.gpai`",
219
+ "body": "classifies general-purpose AI models with the Art. 51(2) FLOP threshold (10^25 cumulative training compute → presumption of systemic risk per Art. 55)."
220
+ },
221
+ {
222
+ "title": "`b.compliance.aiAct.annexIVScaffold(...)`",
223
+ "body": "returns the eight Annex IV documentation sections (general description / detailed description / monitoring + functioning / risk-management / changes / harmonised standards / EU declaration of conformity / post-market monitoring)."
224
+ },
225
+ {
226
+ "title": "`b.compliance.aiAct.deployerChecklist(assessment)`",
227
+ "body": "returns operator-actionable next-steps with article references for the assessment's tier (prohibited → do-not-deploy; high-risk → conformity assessment + technical doc + risk mgmt + data governance + logging + human oversight + Art. 71 EU database + Art. 72 post-market monitoring; limited-risk → transparency middleware mount; GPAI → technical doc + downstream info + copyright policy + training summary + Art. 55 systemic-risk obligations when applicable)."
228
+ },
229
+ {
230
+ "title": "`b.compliance.aiAct.DEADLINES`",
231
+ "body": "carries the Art. 113 phase-in calendar (2026-02-02 prohibited / 2026-08-02 GPAI + transparency / 2027-08-02 high-risk)."
232
+ },
233
+ {
234
+ "title": "`b.middleware.aiActDisclosure({ kind, deployerName, policyUri, mode })`",
235
+ "body": "auto-injects `AI-Act-Notice` / `AI-Act-Article` / `AI-Act-Policy` response headers on every 2xx response, plus an `<div role=\"status\">` banner injection on 2xx HTML responses when mode=`html`; honors `X-Skip-AI-Act` request header and `res.locals.aiActSkip` opt-out. Audit emissions: `compliance.aiact.classified` / `aiact.disclosed` / `aiact.<kind>`. 130+ test cases covering catalog completeness, classifier exemptions, GPAI FLOP threshold, banner / watermark / metaTag round-trips, logging biometric-field enforcement, retention floors, annexIVScaffold sections, deployer checklist for all five tiers."
236
+ }
237
+ ]
238
+ }
239
+ ]
240
+ },
241
+ {
242
+ "version": "0.7.108",
243
+ "date": "2026-05-06",
244
+ "headline": "`b.auth.stepUp` + `b.middleware.requireStepUp` — RFC 9470 step-up authentication",
245
+ "summary": "OAuth 2.0 Step-Up Authentication Challenge with elevation-grant short-circuit, RFC 9396 authorization-details parsing, and RFC 8176 AMR phishing-resistance evaluation. Routes that need a stronger or fresher authentication ceremony now have a complete primitive.",
246
+ "sections": [
247
+ {
248
+ "heading": "Added",
249
+ "items": [
250
+ {
251
+ "title": "`b.auth.acr` ACR-vocabulary registry",
252
+ "body": "Operator-extendable registry with rank-based `meets(presented, required)` and `meetsAny(presented, required[])` comparison plus `register({ value, rank })` for private vocabularies. Ships NIST 800-63-4 AAL1/2/3, OIDC `0`/`1`/`2`, ISO 29115 `loa1`-`loa4`, InCommon Bronze/Silver/Gold, and RFC 9470 `phr`/`phrh`."
253
+ },
254
+ {
255
+ "title": "`b.auth.authTime` freshness helpers",
256
+ "body": "`ageSec(claims)` / `freshEnough(claims, maxAge)` / `buildClaims({ method, prevAt })` (refresh preserves prior auth_time per OIDC Core 1.0 §5.4) / `recommendMaxAge` clamping helper."
257
+ },
258
+ {
259
+ "title": "`b.auth.stepUp.evaluate` full RFC 9470 evaluation",
260
+ "body": "Runs ACR rank check, ACR-values list (any-satisfies), `max_age` freshness, RFC 8176 AMR `requiredAmr: [\"hwk\", \"pop\"]` requirement, and `phishingResistant: true` boolean check. Returns structured `{ ok: false, error, reason }` so the request path keeps moving; operator typos in the requirement bubble up at boot."
261
+ },
262
+ {
263
+ "title": "`buildChallenge` + `parseChallenge`",
264
+ "body": "`buildChallenge({ requirement, realm, errorDescription })` assembles the RFC 7235-quoted `WWW-Authenticate: Bearer error=\"insufficient_user_authentication\", acr_values=\"...\", max_age=\"...\"` value with control-character / quote-injection rejection. `parseChallenge(headerValue)` is the operator-side roundtrip helper."
265
+ },
266
+ {
267
+ "title": "`parseAuthorizationDetails` RFC 9396 parser",
268
+ "body": "Parses the fine-grained authorization-details parameter and validates each entry has the required `type` field."
269
+ },
270
+ {
271
+ "title": "Elevation-grant primitives",
272
+ "body": "`b.auth.stepUp.grant.{create,verify,revoke,isRevoked,list,setSigningKey}` issues short-lived HMAC-SHA3-512 signed elevation grants binding `(subject, scope, acr, amr, audience, evidence, iat, exp, jti)`; `verify` enforces audience, scope, subject, expiry, and revocation."
273
+ },
274
+ {
275
+ "title": "`b.middleware.requireStepUp`",
276
+ "body": "Mounts the RFC 9470 challenge in front of routes; checks an optional `X-Step-Up-Grant` header first (multi-step flows skip re-prompting), falls back to claims-based evaluation. Audit emissions on every state transition: `auth.stepup.required`, `satisfied`, `denied`, `grant.issued`, `consumed`, `revoked`, `rejected`. Sixty-plus test cases cover ACR rank, registration, AMR phishing-resistance, auth_time freshness with skew, RFC 7235 quote-injection rejection, RAR JSON shape checks, grant happy-path / tamper / audience / scope / expiry / revoke, and middleware happy-path / 401-with-WWW-Authenticate / grant short-circuit / scope-mismatch fallback / config-time typo throws."
277
+ }
278
+ ]
279
+ }
280
+ ],
281
+ "references": [
282
+ {
283
+ "label": "RFC 9470 OAuth 2.0 Step-Up Authentication Challenge",
284
+ "url": "https://www.rfc-editor.org/rfc/rfc9470.html"
285
+ },
286
+ {
287
+ "label": "RFC 9396 Rich Authorization Requests",
288
+ "url": "https://www.rfc-editor.org/rfc/rfc9396.html"
289
+ },
290
+ {
291
+ "label": "RFC 8176 Authentication Method Reference Values",
292
+ "url": "https://www.rfc-editor.org/rfc/rfc8176.html"
293
+ },
294
+ {
295
+ "label": "RFC 7235 HTTP Authentication",
296
+ "url": "https://www.rfc-editor.org/rfc/rfc7235.html"
297
+ },
298
+ {
299
+ "label": "NIST SP 800-63-4",
300
+ "url": "https://pages.nist.gov/800-63-4/"
301
+ }
302
+ ]
303
+ },
304
+ {
305
+ "version": "0.7.107",
306
+ "date": "2026-05-06",
307
+ "headline": "`b.auth.sdJwtVc` Selective Disclosure JWT for Verifiable Credentials",
308
+ "summary": "SD-JWT VC primitive aligned with the EU Digital Identity Wallet (EUDI) roll-out and EU AI Act Art. 50 disclosure requirements. Ships issuer, holder, verifier, and key-binding flows with PQC algorithm defaults.",
309
+ "sections": [
310
+ {
311
+ "heading": "Added",
312
+ "items": [
313
+ {
314
+ "title": "`b.auth.sdJwtVc.issue` issuer flow",
315
+ "body": "`issue({ issuer, vct, claims, selectivelyDisclosed, issuerKey, ... })` mints an SD-JWT VC: per-claim disclosures (base64url-encoded `[salt, name, value]` JSON arrays) with their SHA-256 / SHA3-256 / SHA-512 / SHA3-512 digests pinned in the issuer-signed `_sd` array. Selectively-disclosed claims are hidden from the issuer payload; plain claims pass through. Optional `cnf` claim pins the holder's public JWK for key-binding. Supported algorithms: ES256 (default per spec), ES384, EdDSA, ML-DSA-87 (the framework's PQC default), ML-DSA-65."
316
+ },
317
+ {
318
+ "title": "`present` holder presentation builder",
319
+ "body": "`present({ sdJwt, disclosedClaimNames, audience, nonce, holderKey })` selects which disclosures to reveal and signs an optional Key Binding JWT (typ=`kb+jwt`) carrying the audience, nonce, iat, and an `sd_hash` binding it to the specific presentation."
320
+ },
321
+ {
322
+ "title": "`verify` verifier pipeline",
323
+ "body": "`verify(presentation, { issuerKeyResolver, audience, nonce, expectedVct, requireKeyBinding })` runs the full verification pipeline: issuer JWT signature, iat / exp / vct, per-disclosure digest match against the issuer's `_sd` array, and optional KB-JWT signature plus audience, nonce, and `sd_hash` checks."
324
+ },
325
+ {
326
+ "title": "`b.auth.sdJwtVc.issuer.create` operator-side factory",
327
+ "body": "Operator-side issuer factory with key management, `kid` stamping, per-issuance audit emission, and `rotateKey` support."
328
+ },
329
+ {
330
+ "title": "`b.auth.sdJwtVc.holder.create` wallet helper",
331
+ "body": "Operator-pluggable storage (production wires `b.db` / `b.objectStore`; built-in `memoryStorage()` for dev / tests). `store` / `present` / `list` / `get` / `delete` covers the full wallet lifecycle."
332
+ },
333
+ {
334
+ "title": "`b.auth.sdJwtVc.disclosure.encode` / `decode`",
335
+ "body": "Pure helpers for the SD-JWT disclosure format. Audit emissions on every state transition (`auth.sdJwtVc.issued`, `auth.sdJwtVc.holder.stored`, `presented`, `deleted`, `auth.sdJwtVc.key_rotated`). Thirty-two test cases cover disclosure round-trip, issue/verify happy path, signature tamper, expiration, vct mismatch, disclosure tamper, present subset, key binding, issuer factory + key rotation, holder factory + storage round-trip, hash-algorithm switching, and plain-only credentials."
336
+ }
337
+ ]
338
+ }
339
+ ],
340
+ "references": [
341
+ {
342
+ "label": "draft-ietf-oauth-sd-jwt-vc",
343
+ "url": "https://datatracker.ietf.org/doc/draft-ietf-oauth-sd-jwt-vc/"
344
+ },
345
+ {
346
+ "label": "EU Digital Identity Wallet (EUDI)",
347
+ "url": "https://digital-strategy.ec.europa.eu/en/policies/eudi-wallet"
348
+ },
349
+ {
350
+ "label": "EU AI Act (Regulation 2024/1689) Art. 50",
351
+ "url": "https://eur-lex.europa.eu/eli/reg/2024/1689/oj"
352
+ }
353
+ ]
354
+ },
355
+ {
356
+ "version": "0.7.106",
357
+ "date": "2026-05-06",
358
+ "headline": "`b.guardHtml.wcag` audit-only accessibility scanner",
359
+ "summary": "Pure static analysis of HTML against WCAG 2.2 — no rendering, no JS execution. Emits structured findings the operator wires into a CI gate, an audit log, or a dev warning. Ships page-level, ARIA, tables, and forms scanners.",
360
+ "sections": [
361
+ {
362
+ "heading": "Added",
363
+ "items": [
364
+ {
365
+ "title": "`b.guardHtml.wcag.audit` page + element scanner",
366
+ "body": "`audit(html, { level, ignore, allowedRoles, allowedAutocomplete, ... })` returns `{ findings: [{ sc, level, severity, element, line, column, message, remediation }], summary: { error, warning, info }, score, totalFindings, scopeUrl, scannedAt }`. Page-level checks: `<html lang>` (3.1.1), `<title>` (2.4.2), skip-link (2.4.1). Element-level checks: `<img alt>` (1.1.1), input labels (3.3.2), input names (4.1.2), button text (4.1.2), anchor accessible name (2.4.4), heading order, and empty heading (1.3.1)."
367
+ },
368
+ {
369
+ "title": "`b.guardHtml.wcag.aria.audit` WAI-ARIA 1.2 validation",
370
+ "body": "Catches unknown role values, missing required ARIA properties (e.g. `role=\"checkbox\"` without `aria-checked`), aria-* values outside the spec value set (`aria-checked` outside `{true, false, mixed}`), unresolved `aria-labelledby` / `aria-controls` / `aria-describedby` references, and `aria-hidden=\"true\"` on interactive elements. Operators with custom design systems extend the role registry via `allowedRoles`."
371
+ },
372
+ {
373
+ "title": "`b.guardHtml.wcag.tables.audit` table semantics",
374
+ "body": "Flags `<table>` without `<caption>` for data tables (layout tables with `role=\"presentation\"` skip the check), `<th>` without scope or with invalid scope value, and `<tr>` outside table-context wrappers."
375
+ },
376
+ {
377
+ "title": "`b.guardHtml.wcag.forms.audit` form-specific checks",
378
+ "body": "`<fieldset>` without `<legend>` (1.3.1), input `autocomplete=` value against the HTML 5.3 token registry (1.3.5), password input with `autocomplete=\"off\"` (3.3.8 blocks password managers), input/email without autocomplete (3.3.7 redundant entry), and `<textarea>` without a label."
379
+ },
380
+ {
381
+ "title": "Filters + heuristic score",
382
+ "body": "Conformance level filter (`A` / `AA` / `AAA`); `ignore: [\"1.4.3\"]` for SC-by-SC opt-out; `skipAria` / `skipTables` / `skipForms` for module-level opt-out. Heuristic score (1 - weighted-violations / heuristic-max) for a quick gauge. Fifty-one test cases."
383
+ }
384
+ ]
385
+ }
386
+ ],
387
+ "references": [
388
+ {
389
+ "label": "WCAG 2.2",
390
+ "url": "https://www.w3.org/TR/WCAG22/"
391
+ },
392
+ {
393
+ "label": "WAI-ARIA 1.2",
394
+ "url": "https://www.w3.org/TR/wai-aria-1.2/"
395
+ },
396
+ {
397
+ "label": "HTML autocomplete token registry",
398
+ "url": "https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill"
399
+ }
400
+ ]
401
+ },
402
+ {
403
+ "version": "0.7.105",
404
+ "date": "2026-05-06",
405
+ "headline": "`b.compliance.sanctions` screening primitive",
406
+ "summary": "Sanctions-list screening for KYC, payment, and customer-onboarding flows. Framework owns indexing plus three match strategies; operator owns the daily fetch and format-specific parsing. Ships parser shims for OFAC SDN, EU CSL, and UN 1267.",
407
+ "sections": [
408
+ {
409
+ "heading": "Added",
410
+ "items": [
411
+ {
412
+ "title": "`b.compliance.sanctions.create` screener",
413
+ "body": "`create({ entries, algorithm, fuzzy, ... })` returns a screener with `screen(input)` (single record), `screenBulk(inputs)` (batch), `snapshot()` (rule-version digest for audit trails), `reload(newEntries)` (atomic index swap with diff), `entryById(id)`, and `size()`."
414
+ },
415
+ {
416
+ "title": "Three match strategies",
417
+ "body": "`exact` (fastest, no fuzz), `jaro-winkler` (default, threshold 0.85), and `levenshtein` (edit-distance with cap). Match output: `{ match, hits: [{ entryId, name, matchedOn, score, reason, listed, programs }], algorithm, ruleVersion, screenedAt }`."
418
+ },
419
+ {
420
+ "title": "`b.compliance.sanctions.fuzzy` algorithmic core",
421
+ "body": "Pure helpers: `normalize` (Unicode diacritic strip + lowercase + whitespace collapse), `tokenize`, `levenshtein` (cap + early-exit), `jaro` / `jaroWinkler`, `tokenSetSimilarity` (order-invariant bag-of-tokens), `substringContains` (token-bounded), and `initialsMatch`."
422
+ },
423
+ {
424
+ "title": "`b.compliance.sanctions.aliases.expand`",
425
+ "body": "Alias-expansion helper covering nicknames (Bill / William, Mike / Michael), transliteration variants (Mohamed / Mohammed), reverse-order forms (Smith John / Smith, John), and initials (J. Smith). Thirty-two built-in name pairs plus operator-extensible `extraPairs`."
426
+ },
427
+ {
428
+ "title": "`b.compliance.sanctions.fetcher.create` periodic refresh",
429
+ "body": "Worker that runs the operator's `fetch` callback, validates a non-empty result, and atomically reloads the screener via `screener.reload`. Audit emissions on every refresh state: `compliance.sanctions.refresh.started`, `completed`, `skipped`, `failed`."
430
+ },
431
+ {
432
+ "title": "Parser shims for canonical public list formats",
433
+ "body": "`parseOfacCsvRow` / `parseOfacAliasRow` / `mergeAliases` (OFAC SDN), `parseEuCslEntry` (EU Consolidated Sanctions List XML), and `parseUn1267Entry` (UN Security Council XML). The framework intentionally does not vendor the lists themselves — they change daily and have legal-distribution implications."
434
+ },
435
+ {
436
+ "title": "Audit emissions on screen + match",
437
+ "body": "`compliance.sanctions.screened` on every screen call; `compliance.sanctions.matched` when hits > 0. Thirty-nine test cases cover normalize, tokenize, Levenshtein, Jaro-Winkler, token-set, substring, initials, screen modes, type filter, bulk, snapshot, reload, alias expansion, and fetcher tick + failure modes."
438
+ }
439
+ ]
440
+ }
441
+ ],
442
+ "references": [
443
+ {
444
+ "label": "U.S. Treasury OFAC SDN list",
445
+ "url": "https://ofac.treasury.gov/specially-designated-nationals-and-blocked-persons-list-sdn-human-readable-lists"
446
+ },
447
+ {
448
+ "label": "EU Consolidated Sanctions List",
449
+ "url": "https://data.europa.eu/data/datasets/consolidated-list-of-persons-groups-and-entities-subject-to-eu-financial-sanctions"
450
+ },
451
+ {
452
+ "label": "UK HMT consolidated list",
453
+ "url": "https://www.gov.uk/government/publications/financial-sanctions-consolidated-list-of-targets"
454
+ },
455
+ {
456
+ "label": "UN Security Council 1267 list",
457
+ "url": "https://www.un.org/securitycouncil/content/un-sc-consolidated-list"
458
+ }
459
+ ]
460
+ },
461
+ {
462
+ "version": "0.7.104",
463
+ "date": "2026-05-06",
464
+ "headline": "`b.dsr` data-subject-rights workflow primitive",
465
+ "summary": "End-to-end coordinator for GDPR Article 15-22, CCPA, CPRA, LGPD, PIPEDA, and UK-GDPR data-subject requests. Ships ticket lifecycle, posture-aware deadlines, verification ladder, signed receipts, portability bundles, and two ticket-store backends.",
466
+ "sections": [
467
+ {
468
+ "heading": "Added",
469
+ "items": [
470
+ {
471
+ "title": "`b.dsr.create` workflow coordinator",
472
+ "body": "`b.dsr.create({ ticketStore, posture, identityResolver, sources, ... })` returns a workflow instance with full ticket lifecycle. `submit(input)` resolves the subject identity via the operator-supplied `identityResolver`, computes a posture-aware deadline (gdpr 30d / ccpa 45d / lgpd-br 15d / pipl-cn 15d / pipeda-ca 30d / appi-jp 30d / pdpa-sg 30d / uk-gdpr 30d), and persists a pending ticket. `process(ticketId, opts)` orchestrates per-source `query` (for access / portability / rectification) or `erase` (for erasure) callbacks; partial source failures land the ticket in `partially_completed` state with per-source error capture. `cancel` / `reject` (with required reason per GDPR) advance to terminal states. `expireOverdue()` marks deadline-overdue tickets as `expired`."
473
+ },
474
+ {
475
+ "title": "Seven request types covered",
476
+ "body": "`access`, `erasure`, `portability`, `rectification`, `restriction`, `object`, and `automated-decision` are all first-class workflow entry points."
477
+ },
478
+ {
479
+ "title": "Verification ladder per GDPR Art. 12(6)",
480
+ "body": "Three levels (`minimal` / `secondary` / `strong`) with a minimum required level by request type and an operator override. Erasure, portability, and rectification require `secondary` by default."
481
+ },
482
+ {
483
+ "title": "Receipt + portability builders",
484
+ "body": "`buildReceipt(ticketId)` emits a canonical `blamejs.dsr.receipt/1` JSON envelope for completed/cancelled/rejected/expired tickets with an optional `receiptSigner` hook for cryptographic attestation. `buildPortabilityBundle(ticket)` produces the `blamejs.dsr.portability/1` JSON shape with per-source data for access and portability requests."
485
+ },
486
+ {
487
+ "title": "Two ticket-store backends",
488
+ "body": "`memoryTicketStore()` for development and tests; `dbTicketStore({ db, table })` for production (auto-provisions a SQLite table with subject_email + status indexes and a `purgeExpired()` retention sweep)."
489
+ },
490
+ {
491
+ "title": "Audit emissions on every state transition",
492
+ "body": "`dsr.ticket.submitted`, `in_progress`, `completed`, `partial`, `cancelled`, `rejected`, `expired`, plus per-source `dsr.source.queried`, `erased`, and `failed`. Thirty-eight test cases cover submit, process, cancel, reject, list, expire, portability, verification ladder, receipt, and store backends."
493
+ }
494
+ ]
495
+ }
496
+ ],
497
+ "references": [
498
+ {
499
+ "label": "GDPR Articles 15-22",
500
+ "url": "https://gdpr-info.eu/chapter-3/"
501
+ },
502
+ {
503
+ "label": "CCPA / CPRA",
504
+ "url": "https://oag.ca.gov/privacy/ccpa"
505
+ },
506
+ {
507
+ "label": "LGPD (Brazil)",
508
+ "url": "https://www.gov.br/cnpd/pt-br"
509
+ },
510
+ {
511
+ "label": "PIPEDA (Canada)",
512
+ "url": "https://www.priv.gc.ca/en/privacy-topics/privacy-laws-in-canada/the-personal-information-protection-and-electronic-documents-act-pipeda/"
513
+ },
514
+ {
515
+ "label": "UK GDPR",
516
+ "url": "https://ico.org.uk/for-organisations/uk-gdpr-guidance-and-resources/"
517
+ }
518
+ ]
519
+ },
520
+ {
521
+ "version": "0.7.103",
522
+ "date": "2026-05-06",
523
+ "headline": "W3C distributed tracing suite — tracestate / Baggage / OTel-shaped spans / OTLP exporter",
524
+ "summary": "End-to-end OTel-shaped distributed tracing without a vendored OTel SDK. Adds tracestate + Baggage parsers, an OTel-compatible span builder, an OTLP/JSON exporter, HTTP server span middleware, and log correlation that auto-stamps `trace_id` + `span_id` on every log line inside a request handler.",
525
+ "sections": [
526
+ {
527
+ "heading": "Added",
528
+ "items": [
529
+ {
530
+ "title": "`b.observability.traceContext.parseTracestate` / `buildTracestate`",
531
+ "body": "W3C Trace Context section 3.3 vendor data: enforces vendor-key shape (lcase-alnum + `_-*/`, optional `<tenant>@<system>`), value charset (printable ASCII excluding `,` and `=`), 32-entry cap, 512-char total cap, dup-key-keep-first per section 3.3.1.5."
532
+ },
533
+ {
534
+ "title": "`b.observability.baggage.parse` / `build`",
535
+ "body": "W3C Baggage spec parser + builder for operator-supplied context (tenantId, region, experimentId, etc.) propagated across service boundaries. RFC 7230 tchar key grammar, percent-encoded UTF-8 values, optional per-entry properties (`key=value;property=value`), 64-entry / 8192-char caps."
536
+ },
537
+ {
538
+ "title": "`b.observability.tracer.create({ service, resource, onEnd })`",
539
+ "body": "OTel-shaped span builder. `tracer.start(name, opts)` returns a span with `setAttribute` / `setAttributes` / `addEvent` / `recordException` / `setStatus` / `end` / `isRecording` / `toJSON`. OTLP/JSON-compatible output (Trace v1) with `traceId` / `spanId` / `parentSpanId` / `name` / `kind` / `startTimeUnixNano` / `endTimeUnixNano` / `attributes` / `events` / `status` / `resource` / `scope` / `droppedAttributesCount` / `droppedEventsCount`. Attribute caps (128 keys, 1024-char values), event cap (128) per OTLP defaults. `tracer.startChildOf(parent, name)` derives child spans sharing the trace context."
540
+ },
541
+ {
542
+ "title": "`b.observability.tracer.spanToTraceparent(span)`",
543
+ "body": "Emits the canonical W3C `traceparent` for outbound propagation."
544
+ },
545
+ {
546
+ "title": "`b.observability.otlpExporter.create({ endpoint, ... })`",
547
+ "body": "Buffered OTLP/HTTP JSON span exporter. Batches spans (default 200), flushes on size + interval (default 5s), retries 5xx + 408/429 with exponential backoff, drops oldest on queue overflow (default 4096). Custom `fetchImpl` opt for testing or non-default HTTP transports; `allowedProtocols` opt for cleartext dev collectors."
548
+ },
549
+ {
550
+ "title": "`b.middleware.spanHttpServer({ tracer, ... })`",
551
+ "body": "Auto-creates a root server span per HTTP request, populates OTel `SEMCONV.HTTP_*` / `URL_*` / `SERVER_*` / `CLIENT_*` attributes, attaches the span to `req.span`, ends on response close, fires `onEnd(span.toJSON())` for export. `ignorePaths` (string + RegExp) keeps healthz / static-asset routes out of span volume; `captureRequestHeaders` / `captureResponseHeaders` lift named headers into the span as `http.request.header.*` / `http.response.header.*` attributes."
552
+ },
553
+ {
554
+ "title": "`b.middleware.traceLogCorrelation({ logger })`",
555
+ "body": "Wraps a `b.log` instance for the request lifetime so every `info()` / `warn()` / `error()` emission inside the handler auto-includes `trace_id` + `span_id` from the active context (via `req.trace` + `req.span`). Pass-through when no trace context present."
556
+ }
557
+ ]
558
+ },
559
+ {
560
+ "heading": "Changed",
561
+ "items": [
562
+ {
563
+ "title": "`b.middleware.tracePropagate` reads inbound tracestate",
564
+ "body": "Extended to also read inbound `tracestate` and stamp `req.trace.tracestate` as the parsed entries array (or `[]` when missing); when `setResponseHeader: true`, echoes both `traceparent` and `tracestate` on the response."
565
+ },
566
+ {
567
+ "title": "Shared regex constants consolidated across observability + guard surfaces",
568
+ "body": "`safeBuffer.TRACE_ID_HEX_RE` / `SPAN_ID_HEX_RE` / `RFC7230_TCHAR_RE` extracted as shared regex constants; `guard-mime` / `middleware/headers` / `observability` consolidated against the new shared constants."
569
+ }
570
+ ]
571
+ }
572
+ ],
573
+ "references": [
574
+ {
575
+ "label": "W3C Trace Context Level 1",
576
+ "url": "https://www.w3.org/TR/trace-context-1/"
577
+ },
578
+ {
579
+ "label": "W3C Baggage",
580
+ "url": "https://www.w3.org/TR/baggage/"
581
+ },
582
+ {
583
+ "label": "OTLP/HTTP specification",
584
+ "url": "https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md"
585
+ },
586
+ {
587
+ "label": "RFC 7230 tchar grammar",
588
+ "url": "https://www.rfc-editor.org/rfc/rfc7230.html"
589
+ }
590
+ ]
591
+ },
592
+ {
593
+ "version": "0.7.102",
594
+ "date": "2026-05-06",
595
+ "headline": "`b.middleware.tracePropagate` — inbound traceparent ingestion middleware",
596
+ "summary": "Consumes the inbound `traceparent` header per W3C Trace Context and stamps `req.trace = { traceId, parentId, sampled, hadUpstream }` for downstream handlers + propagation into outbound HTTP calls. Composes with the v0.7.101 `b.observability.traceContext` parser/builder.",
597
+ "sections": [
598
+ {
599
+ "heading": "Added",
600
+ "items": [
601
+ {
602
+ "title": "`b.middleware.tracePropagate({ generateIfMissing, ... })`",
603
+ "body": "`generateIfMissing: true` (default) synthesises a fresh trace when the inbound header is missing/malformed and stamps `hadUpstream: false`. `auditOnMissing: true` emits `system.trace.synthesised` audit events on every locally-originated trace. `setResponseHeader: true` echoes the resolved traceparent on the response (useful when the framework is the back-end of an L7 router that wants to log it). Downstream handlers read `req.trace.traceId` and pass it to `traceContext.build({ traceId: req.trace.traceId, parentId: traceContext.newParentId(), sampled: req.trace.sampled })` for the `traceparent` header on upstream calls."
604
+ }
605
+ ]
606
+ }
607
+ ],
608
+ "references": [
609
+ {
610
+ "label": "W3C Trace Context Level 1",
611
+ "url": "https://www.w3.org/TR/trace-context-1/"
612
+ }
613
+ ]
614
+ },
615
+ {
616
+ "version": "0.7.101",
617
+ "date": "2026-05-06",
618
+ "headline": "`b.observability.traceContext` — W3C Trace Context parser + builder",
619
+ "summary": "Vendor-free W3C Trace Context support so operators can propagate trace IDs across an outbound HTTP call without a vendored OTel SDK. Pairs with the SEMCONV constants from v0.7.95 to build OTel-aligned spans on top.",
620
+ "sections": [
621
+ {
622
+ "heading": "Added",
623
+ "items": [
624
+ {
625
+ "title": "`traceContext.parse(headerValue)`",
626
+ "body": "Consumes a `traceparent` HTTP header value (`00-<32hex traceId>-<16hex parentId>-<2hex flags>`) and returns `{ version, traceId, parentId, flags, sampled }` or null on malformed input. Enforces the section 3.2.2.3 / 3.2.2.4 all-zero-rejection rule (zero trace-id and zero parent-id are explicitly forbidden by spec)."
627
+ },
628
+ {
629
+ "title": "`traceContext.build({ traceId, parentId, sampled })`",
630
+ "body": "Produces a v1 `traceparent` header value; throws on bad input shape."
631
+ },
632
+ {
633
+ "title": "`traceContext.newTraceId()` / `newParentId()`",
634
+ "body": "Generate fresh randomized 128-bit / 64-bit hex strings, with the all-zero retry path the spec requires."
635
+ }
636
+ ]
637
+ }
638
+ ],
639
+ "references": [
640
+ {
641
+ "label": "W3C Trace Context Level 1",
642
+ "url": "https://www.w3.org/TR/trace-context-1/"
643
+ }
644
+ ]
645
+ },
646
+ {
647
+ "version": "0.7.100",
648
+ "date": "2026-05-06",
649
+ "headline": "`b.network.tls.expiryMonitor` — periodic CA-trust-store expiry monitor",
650
+ "summary": "Runs `expiringSoon(windowMs)` on a schedule and surfaces expiring CAs via audit events, observability counters, and an optional operator hook. Closes the continuous-trust-monitoring follow-up to the v0.7.26 OCSP/CT release.",
651
+ "sections": [
652
+ {
653
+ "heading": "Added",
654
+ "items": [
655
+ {
656
+ "title": "`b.network.tls.expiryMonitor({ intervalMs, windowMs, onExpiring })`",
657
+ "body": "Emits `network.tls.ca.expiry_check` audit event on every check (with `expiring` count + `total` CA count), `network.tls.ca.expiring` audit event when any CA falls inside the window, and the matching `network.tls.ca.expiring` observability counter. Optional `onExpiring(rows)` operator hook fires on every check that surfaces expiring CAs so operators can wire pager / Slack alerts. Audit metadata captures the expiring CA labels + the earliest `validTo` timestamp so dashboards can compute days-until-first-expiry without re-querying. Returns a handle with `.stop()` for graceful shutdown."
658
+ }
659
+ ]
660
+ }
661
+ ]
662
+ },
663
+ {
664
+ "version": "0.7.99",
665
+ "date": "2026-05-06",
666
+ "headline": "`b.db.integrityCheck` + `b.db.integrityMonitor` — periodic SQLite corruption detection",
667
+ "summary": "On-demand `PRAGMA integrity_check` plus a scheduled monitor for long-running deployments where filesystem-level corruption can develop after boot. The boot-time check added in v0.7.79 continues to run unchanged at `db.init`.",
668
+ "sections": [
669
+ {
670
+ "heading": "Added",
671
+ "items": [
672
+ {
673
+ "title": "`b.db.integrityCheck()`",
674
+ "body": "Runs `PRAGMA integrity_check` against the live database and returns `\"ok\"` on a healthy database, or an array of corruption description strings on damage. Operators wire this into `/healthz` handlers or one-off CLI checks."
675
+ },
676
+ {
677
+ "title": "`b.db.integrityMonitor({ intervalMs, onCorruption })`",
678
+ "body": "Runs the check on a schedule (24h default), emits `system.db.integrity_ok` / `system.db.integrity_corrupt` audit events, and fires the `db.integrity_check_ok` / `db.integrity_check_corrupt` observability counters. Optional `onCorruption(issues)` operator hook fires on every corrupt result so operators can wire pager alerts. Returns a handle with `.stop()` for graceful shutdown."
679
+ }
680
+ ]
681
+ }
682
+ ]
683
+ },
684
+ {
685
+ "version": "0.7.98",
686
+ "date": "2026-05-06",
687
+ "headline": "`b.ntpCheck.monitor` — periodic clock-drift monitor with audit + observability emissions",
688
+ "summary": "Runs `checkDrift` on a schedule and emits audit + observability events on threshold crossings. The boot-time `b.ntpCheck.bootCheck` continues to run unchanged at `db.init`; the monitor exists for long-running deployments where clock-drift can develop after boot (container with no RTC sync, ntpd stopped after boot, etc.).",
689
+ "sections": [
690
+ {
691
+ "heading": "Added",
692
+ "items": [
693
+ {
694
+ "title": "`b.ntpCheck.monitor({ intervalMs, ... })`",
695
+ "body": "Returns a handle with `.stop()` for graceful shutdown. Audit emissions: `system.ntp.checked` (every check), `system.ntp.drift_warn` (drift exceeds warn threshold), `system.ntp.drift_fatal` (drift exceeds fatal threshold), `system.ntp.unreachable` (every server in the list failed to respond). Observability event: `ntp.drift_ms` gauge on every successful check, labeled with the responding server. Optional `onDrift(result)` operator hook fires on every warning/fatal check so operators can wire pager / Slack notifications."
696
+ }
697
+ ]
698
+ }
699
+ ]
700
+ },
701
+ {
702
+ "version": "0.7.97",
703
+ "date": "2026-05-06",
704
+ "headline": "`b.compliance` lookup helpers — posturesByDomain / posturesByJurisdiction / list",
705
+ "summary": "Pure-function lookups over the v0.7.94 `REGIME_MAP` so admin UIs and multi-region deployments can resolve postures by domain or ISO 3166 alpha-2 jurisdiction without iterating the raw map.",
706
+ "sections": [
707
+ {
708
+ "heading": "Added",
709
+ "items": [
710
+ {
711
+ "title": "`b.compliance.posturesByDomain(domain)`",
712
+ "body": "Returns every posture matching the named domain (`\"privacy\"` / `\"health\"` / `\"payment\"` / `\"cybersecurity\"` / `\"financial-reporting\"` / `\"financial-resilience\"` / `\"product-cybersecurity\"` / `\"ai-governance\"` / `\"biometrics\"` / `\"audit-attestation\"`)."
713
+ },
714
+ {
715
+ "title": "`b.compliance.posturesByJurisdiction(jurisdiction)`",
716
+ "body": "Returns postures matching the ISO 3166 alpha-2 code, `EU`, or `international` — useful for multi-region deployments that resolve different posture configs per region."
717
+ },
718
+ {
719
+ "title": "`b.compliance.list()`",
720
+ "body": "Returns every posture as a `{ posture, name, citation, jurisdiction, domain }` row in canonical `KNOWN_POSTURES` order. Admin UIs render the full set as a dropdown / table without iterating `REGIME_MAP` keys themselves."
721
+ }
722
+ ]
723
+ }
724
+ ]
725
+ },
726
+ {
727
+ "version": "0.7.96",
728
+ "date": "2026-05-06",
729
+ "headline": "`b.observability.timed` — wrap a sync/async operation in a duration + outcome counter",
730
+ "summary": "Consolidates the hand-rolled `t0 = Date.now(); ... event(name, 1, { duration_ms, outcome })` pattern that had been repeated at many observability call sites into a single helper. Composes with the existing `b.observability.SEMCONV` constants.",
731
+ "sections": [
732
+ {
733
+ "heading": "Added",
734
+ "items": [
735
+ {
736
+ "title": "`b.observability.timed(name, fn, labels)`",
737
+ "body": "Measures wall-clock duration of a sync or async operation and emits a counter event carrying `duration_ms` in the labels alongside `outcome: \"ok\" | \"fail\"`. Returns the wrapped function's return value verbatim; rethrows on error after emitting the failure event with `error_type` capturing the thrown error's `.name`. Operation name MUST be a stable string (not derived from input) to keep metric cardinality bounded; dynamic per-tenant labels go in the `labels` parameter. Example: `b.observability.timed(\"db.query\", async () => db.query(\"SELECT ...\"), { [SEMCONV.DB_OPERATION_NAME]: \"select\" })`."
738
+ }
739
+ ]
740
+ }
741
+ ]
742
+ },
743
+ {
744
+ "version": "0.7.95",
745
+ "date": "2026-05-06",
746
+ "headline": "`b.observability.SEMCONV` — GenAI + cloud + container attribute coverage",
747
+ "summary": "Twenty-eight new OpenTelemetry semantic-convention attribute names covering generative-AI workloads, vector databases, cloud runtime context, and Kubernetes orchestration. Reference these constants instead of hand-rolling the strings — typos throw at access time instead of producing mis-named span attributes the OTel collector silently drops.",
748
+ "sections": [
749
+ {
750
+ "heading": "Added",
751
+ "items": [
752
+ {
753
+ "title": "GenAI attributes (19)",
754
+ "body": "`GEN_AI_SYSTEM` / `GEN_AI_REQUEST_MODEL` / `GEN_AI_REQUEST_TEMPERATURE` / `GEN_AI_REQUEST_TOP_P` / `GEN_AI_REQUEST_TOP_K` / `GEN_AI_REQUEST_MAX_TOKENS` / `GEN_AI_REQUEST_STOP_SEQUENCES` / `GEN_AI_RESPONSE_MODEL` / `GEN_AI_RESPONSE_ID` / `GEN_AI_RESPONSE_FINISH_REASONS` / `GEN_AI_USAGE_INPUT_TOKENS` / `GEN_AI_USAGE_OUTPUT_TOKENS` / `GEN_AI_USAGE_TOTAL_TOKENS` / `GEN_AI_OPERATION_NAME` / `GEN_AI_TOOL_NAME` / `GEN_AI_TOOL_CALL_ID` / `GEN_AI_AGENT_ID` / `GEN_AI_AGENT_NAME` / `GEN_AI_AGENT_DESCRIPTION`."
755
+ },
756
+ {
757
+ "title": "Vector DB / RAG attributes (3)",
758
+ "body": "`DB_VECTOR_QUERY_TOP_K` / `DB_VECTOR_QUERY_DIMENSIONS` / `DB_VECTOR_QUERY_DISTANCE_METRIC`."
759
+ },
760
+ {
761
+ "title": "Cloud attributes (4)",
762
+ "body": "`CLOUD_PROVIDER` / `CLOUD_REGION` / `CLOUD_ACCOUNT_ID` / `CLOUD_RESOURCE_ID`."
763
+ },
764
+ {
765
+ "title": "Container / Kubernetes attributes (6)",
766
+ "body": "`CONTAINER_ID` / `CONTAINER_IMAGE_NAME` / `CONTAINER_IMAGE_TAG` / `K8S_NAMESPACE_NAME` / `K8S_POD_NAME` / `K8S_DEPLOYMENT_NAME`."
767
+ }
768
+ ]
769
+ }
770
+ ],
771
+ "references": [
772
+ {
773
+ "label": "OpenTelemetry semantic conventions",
774
+ "url": "https://opentelemetry.io/docs/specs/semconv/"
775
+ }
776
+ ]
777
+ },
778
+ {
779
+ "version": "0.7.94",
780
+ "date": "2026-05-06",
781
+ "headline": "`b.compliance.REGIME_MAP` + `b.compliance.describe` — posture name / citation / jurisdiction / domain lookup",
782
+ "summary": "Frozen lookup table mapping each compliance posture to its human-readable name, statutory citation, jurisdiction, and domain. Operators rendering deployment posture in admin UI or audit logs reach for this instead of hand-rolling the table; values track the regulatory text and update with the framework rather than going stale.",
783
+ "sections": [
784
+ {
785
+ "heading": "Added",
786
+ "items": [
787
+ {
788
+ "title": "`b.compliance.REGIME_MAP` + `b.compliance.describe(<posture>)`",
789
+ "body": "`b.compliance.describe(\"hipaa\")` returns `{ name: \"Health Insurance Portability and Accountability Act\", citation: \"Pub. L. 104-191; 45 CFR Parts 160, 162, 164\", jurisdiction: \"US\", domain: \"health\" }`. Covers all 19 postures shipped through v0.7.91 (hipaa / pci-dss / soc2 / sox plus the v0.7.91 expansions: wmhmda / bipa / ccpa / gdpr / dora / nis2 / cra / ai-act / lgpd-br / pipl-cn / appi-jp / pdpa-sg / pipeda-ca / uk-gdpr). The `domain` field categorizes the regime (privacy / health / payment / cybersecurity / financial-reporting / etc.) so operators can render compliance dashboards grouped by domain instead of alphabetical posture."
790
+ }
791
+ ]
792
+ }
793
+ ]
794
+ },
795
+ {
796
+ "version": "0.7.93",
797
+ "date": "2026-05-06",
798
+ "headline": "Adjacent-regulation incident-reporting deadline references exported on `b.dora`",
799
+ "summary": "`b.dora.DEADLINES_NIS2` — NIS2 (Directive (EU) 2022/2555) Art. 23 deadlines: 24h early warning, 72h initial notification, 1 month final report. `b.dora.DEADLINES_CRA` — CRA (Regulation (EU) 2024/2847) Art. 14 deadlines: 24h early warning, 72h initial notification, 14 days final report.",
800
+ "sections": [
801
+ {
802
+ "heading": "Added",
803
+ "items": [
804
+ {
805
+ "title": "`b.dora.DEADLINES_NIS2`",
806
+ "body": "NIS2 (Directive (EU) 2022/2555) Art. 23 deadlines: 24h early warning, 72h initial notification, 1 month final report."
807
+ },
808
+ {
809
+ "title": "`b.dora.DEADLINES_CRA`",
810
+ "body": "CRA (Regulation (EU) 2024/2847) Art. 14 deadlines: 24h early warning, 72h initial notification, 14 days final report."
811
+ },
812
+ {
813
+ "title": "`b.dora.DEADLINES_HIPAA_BREACH`",
814
+ "body": "HIPAA Breach Notification Rule (45 CFR §164.404 / §164.408) deadlines: 60 days for affected individuals, 60 days for HHS Secretary, annual aggregate report by March 1 for sub-500-individual breaches. Operators handling NIS2 / CRA / HIPAA reporting reach for these constants instead of pinning literal hour counts in their workflow code; the values track the regulatory text and update with the framework rather than going stale in operator code. The b.dora factory itself continues to enforce DORA Article 19 deadlines unchanged — operators wiring NIS2 / CRA / HIPAA workflows compose against the deadline constants directly with their own scheduler / submission code."
815
+ }
816
+ ]
817
+ }
818
+ ]
819
+ },
820
+ {
821
+ "version": "0.7.92",
822
+ "date": "2026-05-06",
823
+ "headline": "Retention floors + observability semconv expansion",
824
+ "summary": "`b.retention.complianceFloor(<posture>, candidateMs)` now recognizes `nis2` (3 years — NIS2 Art. 23 incident reporting), `cra` (5 years — CRA Art. 14 vulnerability handling logs), `lgpd-br` (5 years — Brazil fiscal record minimum + LGPD Art.",
825
+ "sections": [
826
+ {
827
+ "heading": "Added",
828
+ "items": [
829
+ {
830
+ "title": "`b.retention.complianceFloor(<posture>, candidateMs)`",
831
+ "body": "now recognizes `nis2` (3 years — NIS2 Art. 23 incident reporting), `cra` (5 years — CRA Art. 14 vulnerability handling logs), `lgpd-br` (5 years — Brazil fiscal record minimum + LGPD Art. 16), `appi-jp` (3 years — Japan APPI handler-of-record), `pdpa-sg` (1 year — PDPA breach notification audit trail), and `uk-gdpr` (6 years — UK ICO guidance + statutory limit alignment). `gdpr` continues to have no fixed minimum (Art. 5(1)(e) is \"no longer than necessary\" — operator-driven)."
832
+ },
833
+ {
834
+ "title": "`b.observability.SEMCONV`",
835
+ "body": "gains RPC attributes (`RPC_SYSTEM` / `RPC_SERVICE` / `RPC_METHOD` / `RPC_GRPC_STATUS_CODE`), additional messaging keys (`MESSAGING_CLIENT_ID` / `MESSAGING_MESSAGE_ID` / `MESSAGING_DESTINATION_PARTITION_ID` / `MESSAGING_BATCH_MESSAGE_COUNT`), network transport (`NETWORK_TRANSPORT` / `NETWORK_CONNECTION_TYPE`), process / runtime identification (`PROCESS_PID` / `PROCESS_RUNTIME_NAME` / `PROCESS_RUNTIME_VERSION`), service identification (`SERVICE_NAME` / `SERVICE_VERSION` / `SERVICE_INSTANCE_ID`), and telemetry SDK self-id (`TELEMETRY_SDK_NAME` / `TELEMETRY_SDK_LANGUAGE` / `TELEMETRY_SDK_VERSION`). Operators wiring the framework's tap into a gRPC-fronted OTel collector or an outbox-fed Kafka topic now reference the canonical attribute names directly without an aliasing table on their side."
836
+ }
837
+ ]
838
+ }
839
+ ]
840
+ },
841
+ {
842
+ "version": "0.7.91",
843
+ "date": "2026-05-06",
844
+ "headline": "Compliance-posture vocabulary expanded + OpenTelemetry semantic-convention attribute table",
845
+ "summary": "`b.compliance.set(<posture>)` now accepts thirteen new posture names: `wmhmda` (Washington My Health My Data Act), `bipa` (Illinois Biometric Information Privacy Act), `ccpa` (California Consumer Privacy Act), `nis2` (EU NIS2 Directive), `cra` (EU Cyber Resilience Act), `ai-a.",
846
+ "sections": [
847
+ {
848
+ "heading": "Added",
849
+ "items": [
850
+ {
851
+ "title": "`b.compliance.set(<posture>)`",
852
+ "body": "now accepts thirteen new posture names: `wmhmda` (Washington My Health My Data Act), `bipa` (Illinois Biometric Information Privacy Act), `ccpa` (California Consumer Privacy Act), `nis2` (EU NIS2 Directive), `cra` (EU Cyber Resilience Act), `ai-act` (EU AI Act), `lgpd-br` (Brazil LGPD), `pipl-cn` (China PIPL), `appi-jp` (Japan APPI), `pdpa-sg` (Singapore PDPA), `pipeda-ca` (Canada PIPEDA), `uk-gdpr` (UK GDPR). Existing `hipaa` / `pci-dss` / `gdpr` / `soc2` / `dora` / `sox` continue to work. Postures map to per-primitive defaults via the existing compliancePosture opt on guards, retention, dora, etc. — operators set the deployment-wide posture once and primitives that key off it pick up the right defaults."
853
+ },
854
+ {
855
+ "title": "`b.observability.SEMCONV`",
856
+ "body": "frozen attribute-name table tracking the OpenTelemetry semantic-convention stable namespace (1.27+). HTTP server attributes (`http.request.method`, `http.response.status_code`, `http.route`, `server.address`, `client.address`), URL (`url.full`, `url.path`, `url.scheme`), database (`db.system`, `db.namespace`, `db.operation.name`, `db.query.text`), messaging (`messaging.system`, `messaging.destination.name`), auth (`user.id`, `session.id`), errors (`error.type`, `exception.type`, `exception.message`). Operators wiring the framework's tap into an OTel SDK reference these constants instead of hand-rolling the names — no aliasing table on the operator side, and string typos throw at access time instead of producing mis-named span attributes that the OTel collector silently drops."
857
+ }
858
+ ]
859
+ }
860
+ ]
861
+ },
862
+ {
863
+ "version": "0.7.90",
864
+ "date": "2026-05-06",
865
+ "headline": "`b.outbox` — transactional outbox primitive for at-least-once event publication without distributed",
866
+ "summary": "transactions. `b.outbox.create({ externalDb, table, publisher, ... })` returns an outbox instance with three core operations: `enqueue(event, txn)` writes the outbox row inside the operator's transaction (using the `txClient` returned by `b.externalDb.transaction`), `start()` spins a polling publisher worker that claims rows via `SELECT ...",
867
+ "sections": [
868
+ {
869
+ "heading": "Added",
870
+ "items": [
871
+ {
872
+ "title": "`b.outbox.create({ externalDb, table, publisher, ... })`",
873
+ "body": "returns an outbox instance with three core operations: `enqueue(event, txn)` writes the outbox row inside the operator's transaction (using the `txClient` returned by `b.externalDb.transaction`), `start()` spins a polling publisher worker that claims rows via `SELECT ... FOR UPDATE SKIP LOCKED` (Postgres) and dispatches to the operator-supplied async `publisher(event)` callback, `stop()` gracefully shuts the worker down. Failed publishes retry with exponential backoff (`retryBackoff: { initialMs, maxMs, factor }`); rows that exceed `maxAttempts` are marked `'dead'` for operator triage and an `system.outbox.deadletter` audit event fires. Schema is operator-managed: `outbox.declareSchema(externalDb)` runs an idempotent `CREATE TABLE IF NOT EXISTS ... (id, topic, payload, key, headers, enqueued_at, next_attempt_at, published_at, attempts, last_error, status)` + a partial index on `(next_attempt_at) WHERE status = 'pending'`. `pendingCount()` / `deadCount()` expose the queue depth + DLQ depth for operator dashboards. Observability events on every state transition (`outbox.enqueued` / `outbox.published` / `outbox.publish-failed` / `outbox.dead-letter`)."
874
+ }
875
+ ]
876
+ }
877
+ ]
878
+ },
879
+ {
880
+ "version": "0.7.89",
881
+ "date": "2026-05-06",
882
+ "headline": "Three additive primitives bundled: TUS resumable uploads, WebAuthn Signal API, DPoP server-issued nonce challenge",
883
+ "summary": "`b.middleware.tusUpload({ mountPath, store, ... })` implements the [tus.io](https://tus.io) v1.0.0 resumable-upload protocol — POST creates uploads, HEAD reports offsets, PATCH appends chunks, DELETE terminates. Supported extensions: `creation`, `creation-with-upload`, `expiration`, `checksum`, `termination`.",
884
+ "sections": [
885
+ {
886
+ "heading": "Added",
887
+ "items": [
888
+ {
889
+ "title": "`b.middleware.tusUpload({ mountPath, store, ... })`",
890
+ "body": "implements the [tus.io](https://tus.io) v1.0.0 resumable-upload protocol — POST creates uploads, HEAD reports offsets, PATCH appends chunks, DELETE terminates. Supported extensions: `creation`, `creation-with-upload`, `expiration`, `checksum`, `termination`. The `checksum` extension defaults to PQC-first algorithms (`sha3-512`, `shake256`) — operators add classical algorithms explicitly via `checksumAlgorithms`. A built-in `b.middleware.tusUpload.memoryStore({ maxSize })` ships for development; production operators implement the `{ create, head, append, setLength, terminate, purgeExpired, getBuffer }` shape against their object-store backend. Bounded chunk collection routes through `safeBuffer.boundedChunkCollector` (cap-enforced at push time, no 10-GiB pre-collect). Concatenation extension (parallel-chunk assembly) deferred — operators that need it compose against their store layer; re-open if a store-layer-only solution proves insufficient."
891
+ },
892
+ {
893
+ "title": "`b.auth.passkey.signalUnknownCredential` / `signalAllAcceptedCredentials` /",
894
+ "body": "add the W3C WebAuthn Signal API descriptor builders — when the browser implements `PublicKeyCredential.signal*`, operators emit the matching JSON descriptor to clean up stale passkeys, refresh user details, and surface revocations without forcing a re-registration. All three validate `rpId` / `userId` / `credentialId` shape (base64url) and refuse `name`/`displayName` longer than 256 chars."
895
+ },
896
+ {
897
+ "title": "`b.middleware.dpop({ requireNonce: true, nonceRotateSec? })`",
898
+ "body": "implements RFC 9449 §8 server-issued DPoP-Nonce challenge — the middleware emits `DPoP-Nonce: <fresh>` on every 401 response, refuses proofs whose `nonce` claim isn't in the rolling current+previous pair, and refreshes the nonce on every successful response. The rolling-pair manager rotates without timers (lazy maybe-rotate on access); no operator nonce store needed. The `getNonce` callback path stays intact for operator-managed nonce flows."
899
+ }
900
+ ]
901
+ }
902
+ ],
903
+ "references": [
904
+ {
905
+ "label": "RFC 9449",
906
+ "url": "https://www.rfc-editor.org/rfc/rfc9449.html"
907
+ }
908
+ ]
909
+ },
910
+ {
911
+ "version": "0.7.88",
912
+ "date": "2026-05-06",
913
+ "headline": "`b.middleware.webAppManifest` + `b.middleware.assetlinks` — two static-content middlewares for PWA",
914
+ "summary": "+ Trusted Web Activity support. `b.middleware.webAppManifest({ name, start_url, icons, ... })` serves the W3C Web App Manifest at `/manifest.webmanifest` (and `/manifest.json` when `alsoAtJsonPath: true`).",
915
+ "sections": [
916
+ {
917
+ "heading": "Added",
918
+ "items": [
919
+ {
920
+ "title": "`b.middleware.webAppManifest({ name, start_url, icons, ... })`",
921
+ "body": "serves the W3C Web App Manifest at `/manifest.webmanifest` (and `/manifest.json` when `alsoAtJsonPath: true`). The framework JSON-serializes once at create() and serves with `Content-Type: application/manifest+json` per the W3C spec + `Cache-Control: public, max-age=86400` + `X-Content-Type-Options: nosniff`. The W3C-spec attribute set is allowlisted (name / short_name / description / start_url / scope / display / display_override / orientation / theme_color / background_color / icons / screenshots / shortcuts / categories / lang / dir / id / prefer_related_applications / related_applications) — typos throw at create. `name`, `start_url`, and at least one icon are required (W3C — installability minimum). HEAD + GET only."
922
+ },
923
+ {
924
+ "title": "`b.middleware.assetlinks({ statements })`",
925
+ "body": "serves Digital Asset Links at `/.well-known/assetlinks.json` per Google's spec — used by Trusted Web Activity, Android App Links, Smart Lock for Passwords, WebAuthn for Android. Validates each statement carries `relation` (non-empty array) and `target` (object). Same Content-Type / Cache-Control / X-Content-Type-Options posture as the security.txt + manifest emitters."
926
+ }
927
+ ]
928
+ }
929
+ ]
930
+ },
931
+ {
932
+ "version": "0.7.87",
933
+ "date": "2026-05-06",
934
+ "headline": "Two route-guard middlewares for API hardening — `b.middleware.requireMethods` and `b.middleware.requireContentType`",
935
+ "summary": "`b.middleware.requireMethods([\"GET\", \"POST\"])` refuses any HTTP method outside the allowlist with `405 Method Not Allowed` + `Allow:` header listing the allowed methods (per RFC 9110 §15.5.6).",
936
+ "sections": [
937
+ {
938
+ "heading": "Fixed",
939
+ "items": [
940
+ {
941
+ "title": "`b.middleware.requireMethods([\"GET\", \"POST\"])`",
942
+ "body": "refuses any HTTP method outside the allowlist with `405 Method Not Allowed` + `Allow:` header listing the allowed methods (per RFC 9110 §15.5.6). Defends against unexpected verb routing — many CVE-class bugs trace to a route handler wired for GET that accidentally accepts arbitrary verbs (PROPFIND, OPTIONS, custom)."
943
+ },
944
+ {
945
+ "title": "`b.middleware.requireContentType([\"application/json\"])`",
946
+ "body": "refuses requests with a body (POST/PUT/PATCH by default) whose `Content-Type` isn't in the allowlist with `415 Unsupported Media Type` + `Accept:` header listing the allowed types (RFC 9110 §15.5.16). Defends against MIME-type confusion — a route that processes JSON shouldn't accept `application/x-www-form-urlencoded` even if the body parses. Both middlewares emit observability events (`middleware.requireMethods.denied` / `middleware.requireContentType.denied`) on every refusal for triage. Operators wanting to enforce content-type on idempotent verbs that DO carry bodies (rare DELETE-with-body shapes) override the default body-method list via `requireContentType(types, { methods })`."
947
+ }
948
+ ]
949
+ }
950
+ ],
951
+ "references": [
952
+ {
953
+ "label": "RFC 9110",
954
+ "url": "https://www.rfc-editor.org/rfc/rfc9110.html"
955
+ }
956
+ ]
957
+ },
958
+ {
959
+ "version": "0.7.86",
960
+ "date": "2026-05-06",
961
+ "headline": "`b.middleware.csrfProtect({ requireJsonContentType: true })` — strict-fetch mode for JSON-only API",
962
+ "summary": "surfaces. State-changing requests (POST/PUT/PATCH/DELETE) without `Content-Type: application/json` are refused before the token check with `CSRF: state-changing requests require Content-Type: application/json.` and audit emission `csrf.denied` with `reason: \"non-JSON content-type: ...\"`.",
963
+ "sections": [
964
+ {
965
+ "heading": "Added",
966
+ "items": [
967
+ {
968
+ "title": "`b.middleware.csrfProtect({ requireJsonContentType: true })` — strict-fetch mode for JSON-only API",
969
+ "body": "`b.middleware.csrfProtect({ requireJsonContentType: true })` — strict-fetch mode for JSON-only API surfaces. State-changing requests (POST/PUT/PATCH/DELETE) without `Content-Type: application/json` are refused before the token check with `CSRF: state-changing requests require Content-Type: application/json.` and audit emission `csrf.denied` with `reason: \"non-JSON content-type: ...\"`. The browser's form-encoded POST shape is the canonical CSRF vector — a malicious page can `<form action=\"/transfer\" method=POST>` a victim into a state-changing request without a preflight. An `application/json` body forces a CORS preflight (the browser refuses to skip it for non-simple Content-Type values), so an attacker without an operator-allowlisted CORS origin can't reach the route at all. Default `false` — operators with HTML form submissions on the same routes (mixed SPA + classic form pages) keep current behavior; pure-fetch API operators opt in."
970
+ }
971
+ ]
972
+ }
973
+ ]
974
+ },
975
+ {
976
+ "version": "0.7.85",
977
+ "date": "2026-05-06",
978
+ "headline": "`b.auth.statusList` — OAuth Token Status List (draft-ietf-oauth-status-list-20). The canonical",
979
+ "summary": "credential-revocation mechanism for SD-JWT VC and OpenID for Verifiable Credentials. An issuer publishes a JWT-wrapped bitstring at a URL; relying parties fetch + check the bit at index N to determine if the credential whose `status_list` claim points at that URL+index is valid / invalid / suspended / application-specific.",
980
+ "sections": [
981
+ {
982
+ "heading": "Added",
983
+ "items": [
984
+ {
985
+ "title": "`b.auth.statusList.create({ size, bits?, fill? })`",
986
+ "body": "allocates a bit-packed buffer (`bits` ∈ {1, 2, 4, 8} per draft §6.1.1, default 1). The returned object exposes `.set(idx, status)` / `.get(idx)` / `.snapshot()` / `.toJwt({ issuer, subject, privateKey, algorithm, expiresInSec?, ... })`."
987
+ },
988
+ {
989
+ "title": "`b.auth.statusList.fromJwt(token, { publicKey | keyResolver, algorithms?, expectedIssuer?, ... })`",
990
+ "body": "verifies the JWT through `b.auth.jwt.verify` and returns `{ list, claims }` — the list exposes `.get(idx)` to check status of an individual credential without decompressing into a separate Buffer. The bitstring is zlib-deflated (RFC 1951 raw deflate per draft §6.1.4) before base64url encoding so a million-entry list collapses to ~125 KB on the wire when most bits are zero. Caps the compressed payload at 1 MiB; operators publishing larger lists shard. Status constants exported as `b.auth.statusList.STATUS_{VALID,INVALID,SUSPENDED,APPLICATION_SPECIFIC}`. Foundational for the v0.7.58 EU AI Act + eIDAS 2.0 wallet slice."
991
+ }
992
+ ]
993
+ }
994
+ ],
995
+ "references": [
996
+ {
997
+ "label": "RFC 1951",
998
+ "url": "https://www.rfc-editor.org/rfc/rfc1951.html"
999
+ }
1000
+ ]
1001
+ },
1002
+ {
1003
+ "version": "0.7.84",
1004
+ "date": "2026-05-06",
1005
+ "headline": "`b.crypto.sri(content, { algorithm? })` — Subresource Integrity hash builder per W3C SRI 1.0",
1006
+ "summary": ". Operators emit `<script integrity=\"sha384-...\">` and `<link integrity=\"sha384-...\">` to defend against CDN compromise + ISP MITM injection — the browser refuses to load a resource whose actual hash diverges from the integrity attribute.",
1007
+ "sections": [
1008
+ {
1009
+ "heading": "Added",
1010
+ "items": [
1011
+ {
1012
+ "title": "`b.crypto.sri(content, { algorithm? })` — Subresource Integrity hash builder per W3C SRI 1.0",
1013
+ "body": "`b.crypto.sri(content, { algorithm? })` — Subresource Integrity hash builder per W3C SRI 1.0. Operators emit `<script integrity=\"sha384-...\">` and `<link integrity=\"sha384-...\">` to defend against CDN compromise + ISP MITM injection — the browser refuses to load a resource whose actual hash diverges from the integrity attribute. Default algorithm is `sha384` (W3C §3.2 — collision margin without sha512's 64-byte overhead); `sha256` and `sha512` also accepted, anything else refused. Accepts `Buffer` / `Uint8Array` / `string` / array of those — array inputs emit multiple space-separated integrity tokens per W3C §3.3 multi-integrity (browser picks the strongest it recognizes). Returns the standard `sha###-<base64>` format ready to paste into the `integrity=\"\"` attribute."
1014
+ }
1015
+ ]
1016
+ }
1017
+ ]
1018
+ },
1019
+ {
1020
+ "version": "0.7.83",
1021
+ "date": "2026-05-06",
1022
+ "headline": "OpenID Connect RP-Initiated Logout + RFC 9126 Pushed Authorization Requests",
1023
+ "summary": "`b.auth.oauth` gains two operator-facing helpers: `endSessionUrl()` builds the IdP-termination redirect URL for `/logout` routes, and `pushAuthorizationRequest()` POSTs authorization parameters out-of-band to the IdP's PAR endpoint and returns the short `request_uri` reference to redirect the browser through.",
1024
+ "sections": [
1025
+ {
1026
+ "heading": "Added",
1027
+ "items": [
1028
+ {
1029
+ "title": "`b.auth.oauth.endSessionUrl()` (OpenID Connect RP-Initiated Logout)",
1030
+ "body": "Builds the URL the operator's `/logout` route redirects the user-agent to so the IdP terminates the session and bounces back to the operator's app. Accepts `{ idTokenHint?, postLogoutRedirectUri?, state?, logoutHint?, uiLocales?, clientId?, extraParams? }`. The IdP's `end_session_endpoint` is read from the OIDC discovery document or operator-supplied at `create({ endSessionEndpoint })`. Throws `auth-oauth/no-end-session-endpoint` when neither the discovery doc nor the operator opts supply the endpoint."
1031
+ },
1032
+ {
1033
+ "title": "`b.auth.oauth.pushAuthorizationRequest()` (RFC 9126 PAR)",
1034
+ "body": "POSTs the authorization-request parameters directly to the IdP's PAR endpoint (mTLS or client-secret authenticated) and returns `{ url, state, nonce, verifier, challenge, requestUri, expiresIn }` — the browser-side redirect URL is `<authorizationEndpoint>?client_id=...&request_uri=...`. Defends against authorization-request parameter tampering by a man-in-the-middle at the user-agent and against URL-length overflow on long authorization requests (the `request_uri` reference is short). The PAR endpoint is read from the discovery doc's `pushed_authorization_request_endpoint` or operator-supplied at `create({ pushedAuthorizationRequestEndpoint })`. Throws `auth-oauth/no-par-endpoint` when neither source supplies it."
1035
+ }
1036
+ ]
1037
+ }
1038
+ ],
1039
+ "references": [
1040
+ {
1041
+ "label": "OpenID Connect RP-Initiated Logout 1.0",
1042
+ "url": "https://openid.net/specs/openid-connect-rpinitiated-1_0.html"
1043
+ },
1044
+ {
1045
+ "label": "RFC 9126 OAuth 2.0 Pushed Authorization Requests",
1046
+ "url": "https://www.rfc-editor.org/rfc/rfc9126.html"
1047
+ }
1048
+ ]
1049
+ },
1050
+ {
1051
+ "version": "0.7.82",
1052
+ "date": "2026-05-06",
1053
+ "headline": "`b.mail.bimi` — BIMI record builder + DNS verifier (RFC 9091)",
1054
+ "summary": "BIMI publishes a sender's brand logo URL in DNS so receiving MTAs can render it next to messages in supported clients (Gmail, Yahoo, Apple Mail). Three primitives ship: a record-shape builder, a DNS fetch + parse, and a standalone parser for operator-supplied TXT bodies.",
1055
+ "sections": [
1056
+ {
1057
+ "heading": "Added",
1058
+ "items": [
1059
+ {
1060
+ "title": "`b.mail.bimi.recordShape({ logoUrl, vmcUrl?, selector? })`",
1061
+ "body": "Produces the canonical `v=BIMI1; l=https://...; a=https://...` TXT-record string per RFC 9091 §4. Both `l=` (SVG logo URL) and `a=` (Verified Mark Certificate URL per RFC 9091 §6) are HTTPS-required (refuses `http://`). All field values are CR/LF/NUL/semicolon-screened so a hostile URL can't inject a record-separator into the published TXT."
1062
+ },
1063
+ {
1064
+ "title": "`b.mail.bimi.fetchPolicy(domain, { selector?, dnsLookup? })`",
1065
+ "body": "Queries `<selector>._bimi.<domain>` (default selector `\"default\"`) and returns the structured record `{ v, l, a }` or `null` when no policy is published or the record is malformed. Layered on a passing DMARC posture; operators with the existing `b.mail.dmarc` posture set up benefit from BIMI rendering immediately on receivers that honor it."
1066
+ },
1067
+ {
1068
+ "title": "`b.mail.bimi.parseRecord(text)`",
1069
+ "body": "Parses any operator-supplied TXT body — semicolon-separated `key=value` pairs per RFC 9091 §4. The framework does not validate SVG or VMC contents against the RFC §5/§6 profiles; operators feed those to their own asset pipeline. The fetch primitive is a thin DNS lookup that returns the structured record so an operator dashboard or SMTP send-time preflight can verify the publication."
1070
+ }
1071
+ ]
1072
+ }
1073
+ ],
1074
+ "references": [
1075
+ {
1076
+ "label": "RFC 9091 BIMI",
1077
+ "url": "https://www.rfc-editor.org/rfc/rfc9091.html"
1078
+ }
1079
+ ]
1080
+ },
1081
+ {
1082
+ "version": "0.7.81",
1083
+ "date": "2026-05-06",
1084
+ "headline": "`b.middleware.hostAllowlist` — DNS rebinding defense",
1085
+ "summary": "Refuses requests whose `Host` header doesn't match the operator-supplied allowlist. Closes the DNS rebinding chain (attacker DNS flips `evil.com` → `127.0.0.1`, browser still believes the URL string says `evil.com` so same-origin policy lets the JS read the response, but the operator's localhost is what actually serves).",
1086
+ "sections": [
1087
+ {
1088
+ "heading": "Added",
1089
+ "items": [
1090
+ {
1091
+ "title": "`b.middleware.hostAllowlist({ hosts, denyStatus?, denyBody?, audit? })`",
1092
+ "body": "Operators pass an allowlist of canonical Host values (with or without port). Wildcard-leading entries (`*.example.com`) match any single label; `app.sub.example.com` does NOT match `*.example.com` (multi-label is rejected by design — wildcard certificate authorities issue only single-label intermediates). Entries without a port match any port; entries with a port require exact match. Default `denyStatus: 421` (RFC 7540 §9.1.2 \"Misdirected Request\"); default `denyBody: \"Misdirected Request\"`. Audit emits `network.host_allowlist.denied` with the reason (`missing-host` / `host-not-in-allowlist`) and the actual Host value for triage. Operators running explicitly-public services that accept arbitrary subdomains (multi-tenant forum shapes) skip this middleware entirely; there is no per-request opt-out."
1093
+ }
1094
+ ]
1095
+ }
1096
+ ],
1097
+ "references": [
1098
+ {
1099
+ "label": "RFC 7540 §9.1.2 Misdirected Request",
1100
+ "url": "https://www.rfc-editor.org/rfc/rfc7540.html#section-9.1.2"
1101
+ }
1102
+ ]
1103
+ },
1104
+ {
1105
+ "version": "0.7.80",
1106
+ "date": "2026-05-06",
1107
+ "headline": "`b.middleware.securityTxt` (RFC 9116) + SQLite `secure_delete=ON` at DB boot",
1108
+ "summary": "Operators serve a static `/.well-known/security.txt` so security researchers know where to find the disclosure policy, and SQLite databases now overwrite freed page bytes on delete instead of just unlinking them from the B-tree.",
1109
+ "sections": [
1110
+ {
1111
+ "heading": "Added",
1112
+ "items": [
1113
+ {
1114
+ "title": "`b.middleware.securityTxt({ contact, expires, encryption?, policy?, ack?, preferredLanguages?, hiring?, canonical?, alsoAtRoot?, audit? })`",
1115
+ "body": "Serves a static body at `/.well-known/security.txt` (and root `/security.txt` when `alsoAtRoot: true`) per RFC 9116. The `Contact:` and `Expires:` fields are REQUIRED per §2.5; the framework throws at config-time when either is missing AND when `expires` is in the past (RFC 9116 §2.5.5). All field values are CR/LF/NUL-screened (header-injection defense). The body is built once at `create()` and served with `Content-Length` + `Cache-Control: public, max-age=86400` + `X-Content-Type-Options: nosniff`."
1116
+ }
1117
+ ]
1118
+ },
1119
+ {
1120
+ "heading": "Changed",
1121
+ "items": [
1122
+ {
1123
+ "title": "SQLite `PRAGMA secure_delete=ON` applied at `b.db.init`",
1124
+ "body": "SQLite normally just unlinks rows from the B-tree; the underlying page bytes survive on disk until a new write reuses the slot. With `secure_delete=ON`, freed pages are overwritten with zeros so a forensic recovery against the encrypted database file can't reconstruct deleted rows. The cost is one extra write per delete — already dominated by the framework's audit-chain emissions on every DSR erase / cascade fan-out."
1125
+ }
1126
+ ]
1127
+ }
1128
+ ],
1129
+ "references": [
1130
+ {
1131
+ "label": "RFC 9116 security.txt",
1132
+ "url": "https://www.rfc-editor.org/rfc/rfc9116.html"
1133
+ },
1134
+ {
1135
+ "label": "SQLite PRAGMA secure_delete",
1136
+ "url": "https://www.sqlite.org/pragma.html#pragma_secure_delete"
1137
+ }
1138
+ ]
1139
+ },
1140
+ {
1141
+ "version": "0.7.79",
1142
+ "date": "2026-05-06",
1143
+ "headline": "PQC TLS handshake key shares + DB hardening (integrity check, lock-holder boot token)",
1144
+ "summary": "The framework's app-layer envelope has been PQC-first since v0.7.28 (ML-KEM-1024 + X25519 hybrid for sealed records); this release extends PQC posture down to the TLS handshake itself. SQLite integrity check at boot catches B-tree corruption before mid-query failure, and the migration-lock holder ID now carries a per-process boot token so PID-reuse across container restart can't be misattributed.",
1145
+ "sections": [
1146
+ {
1147
+ "heading": "Added",
1148
+ "items": [
1149
+ {
1150
+ "title": "`b.network.tls.pqc` — operator-facing TLS 1.3 key-share configuration",
1151
+ "body": "`b.network.tls.pqc.setKeyShares([\"X25519MLKEM768\", \"X25519\", \"secp256r1\"])` configures the TLS 1.3 key-share groups the framework's `https.Server` / `https.Agent` advertises. The first listed group is the operator priority; the peer picks the first mutually supported entry. `X25519MLKEM768` is the IETF draft-kwiatkowski-tls-ecdhe-mlkem-02 hybrid KEM that negotiates post-quantum + classical in one handshake — forward-secrecy survives both classical-CRQC and future quantum cryptanalysis. The default list attempts hybrid first, falls back to classical X25519 with peers that don't support the hybrid (most of the public web today), and to `secp256r1` for legacy peers. Operators wanting classical-only call `setKeyShares([\"X25519\"])`; `resetKeyShares()` restores the default. Requires Node 24+ with OpenSSL 3.5+ for the X25519MLKEM768 group; older Node falls back silently to the classical entries."
1152
+ }
1153
+ ]
1154
+ },
1155
+ {
1156
+ "heading": "Changed",
1157
+ "items": [
1158
+ {
1159
+ "title": "`b.network.tls.applyToContext({ base })` threads PQC key shares",
1160
+ "body": "Now threads the configured key-share list through as the `groups` option to Node's TLS context. Operators who explicitly set `groups` in their `base` config keep the override."
1161
+ },
1162
+ {
1163
+ "title": "`b.db.init` runs `PRAGMA integrity_check` at boot",
1164
+ "body": "Refuses boot if SQLite reports anything other than `\"ok\"`. Catches B-tree corruption at boot rather than letting it surface mid-query when the engine stumbles on a bad page. Skip via `opts.skipIntegrityCheck: true` for tmpfs-only fixtures (audited reason)."
1165
+ }
1166
+ ]
1167
+ },
1168
+ {
1169
+ "heading": "Fixed",
1170
+ "items": [
1171
+ {
1172
+ "title": "Migration-lock holder ID gains a per-process boot token",
1173
+ "body": "`lib/external-db-migrate.js:_lockHolderId()` now appends a per-process random 8-byte boot token, so a recycled PID-on-hostname slot after a container restart can't be misattributed back to the new boot when reading stale lock rows. Closes the PID-reuse-across-container-restart concurrent-ownership window."
1174
+ }
1175
+ ]
1176
+ }
1177
+ ],
1178
+ "references": [
1179
+ {
1180
+ "label": "draft-kwiatkowski-tls-ecdhe-mlkem",
1181
+ "url": "https://datatracker.ietf.org/doc/draft-kwiatkowski-tls-ecdhe-mlkem/"
1182
+ }
1183
+ ]
1184
+ },
1185
+ {
1186
+ "version": "0.7.78",
1187
+ "date": "2026-05-06",
1188
+ "headline": "`b.cloudEvents.wrap` / `.parse` — CloudEvents 1.0 envelope",
1189
+ "summary": "Vendor-neutral event-format spec adopted by AWS EventBridge, Knative, Azure Event Grid, Google Eventarc, Datadog, and the broader CNCF event ecosystem. Operators wrap outbound events from webhook / pubsub / queue boundaries to interop with these consumers without each consumer learning a bespoke shape.",
1190
+ "sections": [
1191
+ {
1192
+ "heading": "Added",
1193
+ "items": [
1194
+ {
1195
+ "title": "`b.cloudEvents.wrap({ source, type, data?, subject?, time?, id?, datacontenttype?, dataschema?, extensions? })`",
1196
+ "body": "Produces a CloudEvents 1.0 envelope: required attributes (`id` auto-minted as RFC 4122 v4 UUID when omitted, `source`, `specversion=\"1.0\"`, `type`, `time` auto-set to `new Date().toISOString()`), optional attributes (`subject`, `datacontenttype` auto-set to `\"application/json\"` when `data` is JSON-serializable or `\"application/octet-stream\"` when `data` is a `Buffer` — base64-encoded into `data_base64`, `dataschema`), plus operator-defined extension attributes that conform to the §3.1 naming rules (lowercase ASCII alnum, 1-20 chars)."
1197
+ },
1198
+ {
1199
+ "title": "`b.cloudEvents.parse(envelope)`",
1200
+ "body": "Validates the envelope shape and returns a structured form with `extensions` surfaced as a separate object so consumers can route on operator-defined fields without grepping the envelope. Refuses `data` + `data_base64` together (CloudEvents §3.1.1), unsupported specversion, missing required attributes, malformed extension names."
1201
+ }
1202
+ ]
1203
+ },
1204
+ {
1205
+ "heading": "Fixed",
1206
+ "items": [
1207
+ {
1208
+ "title": "`testAuditSafeEmitRedacts` smoke fixture registers its audit namespace",
1209
+ "body": "The smoke fixture introduced in v0.7.75 now registers the `test` audit namespace before emitting so the audit handler's noise log line (`namespace 'test' is not registered`) no longer appears in CI smoke output."
1210
+ }
1211
+ ]
1212
+ }
1213
+ ],
1214
+ "references": [
1215
+ {
1216
+ "label": "CloudEvents v1.0 spec",
1217
+ "url": "https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md"
1218
+ }
1219
+ ]
1220
+ },
1221
+ {
1222
+ "version": "0.7.77",
1223
+ "date": "2026-05-06",
1224
+ "headline": "Argon2 switched from vendored prebuilds to Node's built-in `crypto.argon2*`",
1225
+ "summary": "Targets Node 24+. The framework's `lib/vendor/argon2/` directory (with the `argon2.cjs` bundle and the `prebuilds/` tree of platform-specific `.glibc.node` / `.musl.node` artifacts for darwin-arm64 / darwin-x64 / freebsd-arm64 / freebsd-x64 / linux-arm / linux-arm64 / linux-x64 / win32-x64) is deleted.",
1226
+ "sections": [
1227
+ {
1228
+ "heading": "Added",
1229
+ "items": [
1230
+ {
1231
+ "title": "Argon2 switched from vendored prebuilds to Node's built-in `crypto",
1232
+ "body": "Argon2 switched from vendored prebuilds to Node's built-in `crypto.argon2*` (Node 24+). The framework's `lib/vendor/argon2/` directory (with the `argon2.cjs` bundle and the `prebuilds/` tree of platform-specific `.glibc.node` / `.musl.node` artifacts for darwin-arm64 / darwin-x64 / freebsd-arm64 / freebsd-x64 / linux-arm / linux-arm64 / linux-x64 / win32-x64) is deleted. New `lib/argon2-builtin.js` is a thin wrapper over `crypto.argon2Sync` that produces and parses the PHC string format (`$argon2id$v=19$m=...,t=...,p=...$<salt>$<hash>`). Wire-format compatibility preserved: existing rows in operator databases continue to verify. Behavior preserved: `b.auth.password.hash` / `.verify` / `.needsRehash` retain their async signatures and return shapes; `b.vault.wrap` and `b.backupCrypto.deriveKey` use the same `raw: true` path for raw-bytes output. Operators wanting to supply their own argon2 implementation (pinned upstream, hardware-accelerated, etc.) override at the call site via `opts.argon2` — the supplied object MUST expose the same `hash` / `verify` / `needsRehash` shape. Drops ~440 KB of platform-specific native prebuilds from the repository and shipped npm tarball; eliminates a supply-chain hop. The vendor manifest's `argon2` entry is removed; `scripts/vendor-update.sh argon2` now refuses with a pointer to `lib/argon2-builtin.js`."
1233
+ }
1234
+ ]
1235
+ }
1236
+ ]
1237
+ },
1238
+ {
1239
+ "version": "0.7.76",
1240
+ "date": "2026-05-06",
1241
+ "headline": "CVE-class web hardening sweep — Trojan Source (CVE-2021-42574) log defense + drive-by-MIME attachment opt for `staticServe`",
1242
+ "summary": "Trojan Source defense in `b.log` output: every log line now post-processes Unicode bidi / format-control characters (U+061C / U+200E-200F / U+202A-202E / U+2066-2069) into their `\\uXXXX` literal escape on the wire so a hostile log message can't re-order the visible line in a TTY / syslog / file reader.",
1243
+ "sections": [
1244
+ {
1245
+ "heading": "Fixed",
1246
+ "items": [
1247
+ {
1248
+ "title": "`staticServe.create({ safeAttachmentForRiskyMimes: true })`",
1249
+ "body": "opt-in flag that adds `Content-Disposition: attachment` to responses whose Content-Type is in the risky-inline-MIME set (`text/html`, `text/xml`, `application/xml`, `application/xhtml+xml`, `image/svg+xml`, `application/javascript`, `text/javascript`, `application/x-javascript`). Defends against drive-by execution of user-uploaded HTML / JS / SVG (CVE-2017-15012 SVG XSS / CVE-2009-1312 HTML drive-by class). Default `false` — operators serving framework asset bundles continue to render inline; operators serving user-content directories opt in. Filename is RFC 5987-encoded (ASCII filename + `filename*=UTF-8''...`) so non-ASCII filenames survive without allowing CR/LF header injection."
1250
+ }
1251
+ ]
1252
+ }
1253
+ ],
1254
+ "references": [
1255
+ {
1256
+ "label": "RFC 5987",
1257
+ "url": "https://www.rfc-editor.org/rfc/rfc5987.html"
1258
+ },
1259
+ {
1260
+ "label": "CVE-2021-42574",
1261
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-42574"
1262
+ },
1263
+ {
1264
+ "label": "CVE-2017-15012",
1265
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-15012"
1266
+ },
1267
+ {
1268
+ "label": "CVE-2009-1312",
1269
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2009-1312"
1270
+ }
1271
+ ]
1272
+ },
1273
+ {
1274
+ "version": "0.7.75",
1275
+ "date": "2026-05-06",
1276
+ "headline": "Audit-emit redaction pipeline",
1277
+ "summary": "`b.audit.safeEmit` now scrubs the `actor` / `reason` / `metadata` fields through `b.redact.redact()` before they hit the audit handler.",
1278
+ "sections": [
1279
+ {
1280
+ "heading": "Added",
1281
+ "items": [
1282
+ {
1283
+ "title": "Audit-emit redaction pipeline",
1284
+ "body": "Audit-emit redaction pipeline. `b.audit.safeEmit` now scrubs the `actor` / `reason` / `metadata` fields through `b.redact.redact()` before they hit the audit handler. Operators who pass `metadata: { reason: e.message }` from a caught error could land DB connection strings, bearer tokens, JWT compact-serialization fixtures, AWS access keys, PEM private keys, SSH keys, credit cards, and SSNs in audit rows; the redact pipeline catches the common shapes (sensitive field names + value-shape detectors) and replaces them with markers (`[REDACTED]`, `[REDACTED-CONN-STRING]`, `[REDACTED-JWT]`, `[REDACTED-PEM]`, `[REDACTED-AWS-KEY]`, `[REDACTED-CC]`, `[REDACTED-SSN]`, `[REDACTED-SEALED]`, `[REDACTED-SSH-KEY]`). The `b.redact` primitive existed in lib/ since v0.4.x but was dead code — `audit.safeEmit` previously passed metadata through unchanged. New connection-string detector matches `protocol://user:pass@host` shapes that surface in error messages from external-DB / SMTP / HTTP drivers (RFC 3986 generic syntax). Drop-silent on redact failure — the pipeline never breaks the caller's audit attempt."
1285
+ }
1286
+ ]
1287
+ }
1288
+ ],
1289
+ "references": [
1290
+ {
1291
+ "label": "RFC 3986",
1292
+ "url": "https://www.rfc-editor.org/rfc/rfc3986.html"
1293
+ }
1294
+ ]
1295
+ },
1296
+ {
1297
+ "version": "0.7.74",
1298
+ "date": "2026-05-06",
1299
+ "headline": "Email receive-side parity: DMARC aggregate (RUA) report parser + ARC trust evaluation + Authentication-Results header builder",
1300
+ "summary": "Closes the \"framework can send mail compliantly but can't receive compliantly\" gap. `b.mail.dmarc.parseAggregateReport(xmlBytes, { contentType? })` parses RFC 7489 §7.2 aggregate XML reports through the framework's existing `lib/parsers/safe-xml.js` (the existing security-focused XML parser handles XXE / DOCTYPE / entity-expansion defenses by default).",
1301
+ "sections": [
1302
+ {
1303
+ "heading": "Added",
1304
+ "items": [
1305
+ {
1306
+ "title": "`b.mail.dmarc.parseAggregateReport(xmlBytes, { contentType? })`",
1307
+ "body": "parses RFC 7489 §7.2 aggregate XML reports through the framework's existing `lib/parsers/safe-xml.js` (the existing security-focused XML parser handles XXE / DOCTYPE / entity-expansion defenses by default). Auto-detects gzip via magic bytes (`0x1f 0x8b`) or `Content-Type: application/gzip`. Returns `{ reportMetadata, policyPublished, records, totals }` with per-record source-IP / count / policy-evaluated dispositions / identifiers / DKIM + SPF auth results, plus aggregated `messages` / `aligned` / `notAligned` totals operators want for dashboards. Caps report size at 8 MiB and records-per-report at 10 000."
1308
+ },
1309
+ {
1310
+ "title": "`b.mail.arc.evaluate(rfc822, { trustedSealers })`",
1311
+ "body": "wraps the existing `arc.verify` cryptographic chain check with the operator-side trust decision: given a passing chain, did any hop in the chain belong to a sealer the operator trusts? Returns `{ chainStatus, trusted, trustedHop, trustedDomain }` walking hops most-recent-first so the deepest trusted sealer wins."
1312
+ },
1313
+ {
1314
+ "title": "`b.mail.authResults.emit({ authservId, results, fold? })`",
1315
+ "body": "builds the RFC 8601 Authentication-Results header value — operators consume per-method results from `b.mail.spf.verify` / `b.mail.dmarc.evaluate` / `b.mail.arc.verify`, hand them to `.emit`, and the framework formats the conformant header string with method-specific properties (`smtp.mailfrom`, `header.d`, `header.from`, `policy.iprev`, `policy.ip`, `policy.tls`). Refuses unknown methods / results at config-mistake time."
1316
+ },
1317
+ {
1318
+ "title": "`b.network.smtp.tlsRpt.parseReport(body, { contentType? })`",
1319
+ "body": "is the receive-side counterpart to `tlsRpt.recordShape` / `tlsRpt.submit` — accepts a Buffer or string, auto-detects gzip, parses JSON, validates the RFC 8460 §4.4 required-fields shape (`organization-name`, `date-range`, `report-id`, `policies`), aggregates `total-successful-session-count` / `total-failure-session-count` across policies. Caps report size at 8 MiB and policies-per-report at 1024."
1320
+ }
1321
+ ]
1322
+ }
1323
+ ],
1324
+ "references": [
1325
+ {
1326
+ "label": "RFC 7489",
1327
+ "url": "https://www.rfc-editor.org/rfc/rfc7489.html"
1328
+ },
1329
+ {
1330
+ "label": "RFC 822",
1331
+ "url": "https://www.rfc-editor.org/rfc/rfc822.html"
1332
+ },
1333
+ {
1334
+ "label": "RFC 8601",
1335
+ "url": "https://www.rfc-editor.org/rfc/rfc8601.html"
1336
+ },
1337
+ {
1338
+ "label": "RFC 8460",
1339
+ "url": "https://www.rfc-editor.org/rfc/rfc8460.html"
1340
+ }
1341
+ ]
1342
+ },
1343
+ {
1344
+ "version": "0.7.73",
1345
+ "date": "2026-05-06",
1346
+ "headline": "`b.auth.aal` + `b.middleware.requireAal({ minimum })` — NIST SP 800-63-4 Authentication Assurance",
1347
+ "summary": "Level bands. `b.auth.aal.fromMethods({ password, totp, webauthn, ...",
1348
+ "sections": [
1349
+ {
1350
+ "heading": "Added",
1351
+ "items": [
1352
+ {
1353
+ "title": "`b.auth.aal.fromMethods({ password, totp, webauthn, ... })`",
1354
+ "body": "combines a set of operator-asserted authenticator methods into the resulting band: `AAL1` (single factor — memorized secret OR single-factor cryptographic), `AAL2` (multi-factor — memorized secret + OTP/SMS/hardware/mTLS), `AAL3` (phishing-resistant multi-factor — WebAuthn / passkey / hardware-+-PIN). Recognized methods: `password`, `pin`, `totp`, `sms`, `webauthn`, `passkey`, `hardware`, `mtls`."
1355
+ },
1356
+ {
1357
+ "title": "`b.middleware.requireAal({ minimum, getAal?, audit?, realm? })`",
1358
+ "body": "gates routes by the request's AAL band — reads `req.user.aal` by default (or operator-supplied `getAal(req)`), compares against the minimum, returns 401 with `WWW-Authenticate: AAL-StepUp realm=\"...\", required=\"AAL2\"` on insufficient assurance. The bespoke scheme name signals to the operator's frontend that a step-up flow should be triggered (re-prompt for TOTP / passkey) without reusing the generic `Bearer` challenge namespace. Audit emits `auth.aal.granted` / `auth.aal.denied` (drop-silent on observability sink failure). The framework leaves AMR/ACR claim emission to the operator's IdP; the new `b.auth.aal.AMR` constants object provides consistent OIDC-conformant strings for operators emitting access tokens with AAL info. Also exposes `b.auth.aal.meets(actual, required)` for ad-hoc band comparisons outside the middleware. CI vendor-manifest gate fix: vendor files now have an explicit `.gitattributes` `lib/vendor/ -text binary` declaration so git never rewrites line endings between Windows and Linux checkouts. The `lib/vendor/noble-ciphers.cjs` file was renormalized to LF on disk and re-hashed in `lib/vendor/MANIFEST.json`. Closes the v0.7.65–0.7.72 npm-publish.yml smoke-test failures (`vendor manifest: @noble/ciphers :: server hash matches`)."
1359
+ }
1360
+ ]
1361
+ }
1362
+ ]
1363
+ },
1364
+ {
1365
+ "version": "0.7.72",
1366
+ "date": "2026-05-06",
1367
+ "headline": "`b.mail` gains EAI / SMTPUTF8 / Punycode-IDN support (RFC 6531 / 6532 / 6533 / 3492)",
1368
+ "summary": ". Internationalized email addresses (`müller@münchen.example`) now validate through `b.mail.send()` — `_isValidEmail` detects non-ASCII content, converts the IDN domain to Punycode via Node's `url.domainToASCII`, and re-tests the assembled `local@ascii-domain` against the framework's pragmatic email regex.",
1369
+ "sections": [
1370
+ {
1371
+ "heading": "Added",
1372
+ "items": [
1373
+ {
1374
+ "title": "`b.mail.toAscii(domain)`",
1375
+ "body": "and. (See CHANGELOG for full context)."
1376
+ },
1377
+ {
1378
+ "title": "`b.mail.toUnicode(domain)`",
1379
+ "body": "are the operator-facing wrappers around `node:url`'s IDN helpers — one obvious place to reach for Punycode encoding when handling addresses outside `send()`. SMTP transport now captures EHLO extension lines (250-X continuations) and detects `SMTPUTF8`. Messages whose from / to / cc / bcc / subject contain non-ASCII octets compute `requiresSmtpUtf8 = true`; when the peer advertises `SMTPUTF8` the transport appends ` SMTPUTF8` to `MAIL FROM:<...>` per RFC 6531 §3.4. When the peer does NOT advertise `SMTPUTF8` AND the message requires it, the transport refuses with `mail/smtp-failed: eai-required-not-supported` rather than emit a mangled wire (some peers would silently corrupt headers downstream). Pure-ASCII messages continue without the keyword (some legacy mailboxes reject `SMTPUTF8` outright on transactions that don't need it)."
1380
+ }
1381
+ ]
1382
+ }
1383
+ ],
1384
+ "references": [
1385
+ {
1386
+ "label": "RFC 6531",
1387
+ "url": "https://www.rfc-editor.org/rfc/rfc6531.html"
1388
+ }
1389
+ ]
1390
+ },
1391
+ {
1392
+ "version": "0.7.71",
1393
+ "date": "2026-05-06",
1394
+ "headline": "`b.auth.oauth` adopts the OAuth 2.1 (draft-ietf-oauth-v2-1) baseline. `pkce: false` is now",
1395
+ "summary": "now refused outright — `create()` throws `auth-oauth/pkce-required` rather than warning-and-continuing. PKCE is required for every client (public AND confidential) per OAuth 2.1; the prior pre-1.0 leniency that emitted a warning and proceeded is closed.",
1396
+ "sections": [
1397
+ {
1398
+ "heading": "Added",
1399
+ "items": [
1400
+ {
1401
+ "title": "`pkce: false` is now refused outright",
1402
+ "body": "`create()` throws `auth-oauth/pkce-required` rather than warning-and-continuing. PKCE is required for every client (public AND confidential) per OAuth 2.1; the prior pre-1.0 leniency that emitted a warning and proceeded is closed. Operators integrating with genuinely-broken legacy IdPs that don't accept `code_challenge` must strip the parameters at their own ingress; the framework primitive does not ship that escape hatch. The framework's authorization-code flow already implements the rest of the OAuth 2.1 baseline (`response_type=code` only — no implicit / password / client-credentials grants exposed; mandatory `state`; HTTPS-required `redirect_uri` outside localhost dev opt-in). Wiki docstring updated to reflect the lock-in."
1403
+ }
1404
+ ]
1405
+ }
1406
+ ]
1407
+ },
1408
+ {
1409
+ "version": "0.7.70",
1410
+ "date": "2026-05-06",
1411
+ "headline": "`b.auth.dpop` + `b.middleware.dpop` — RFC 9449 Demonstrating Proof of Possession. Bearer tokens are",
1412
+ "summary": "bound to a per-client keypair, so an attacker who exfils the access token still can't replay it without stealing the client's private key.",
1413
+ "sections": [
1414
+ {
1415
+ "heading": "Added",
1416
+ "items": [
1417
+ {
1418
+ "title": "`b.auth.dpop.buildProof({ htm, htu, privateKey, accessToken?, nonce?, jti?, iat? })`",
1419
+ "body": "produces a compact-JWS proof with the public key embedded in the header (`typ: \"dpop+jwt\"`, `jwk`); the proof signs over the request method + canonicalized URI + a fresh `jti`, optionally binding to `ath = sha256(access_token)` and a server-issued nonce."
1420
+ },
1421
+ {
1422
+ "title": "`b.auth.dpop.verify(proof, { htm, htu, algorithms?, iatWindowSec?, accessToken?,",
1423
+ "body": "validates the proof: typ check, alg-allowlist (HS*/none refused outright), signature verified against the embedded jwk, htm/htu match, iat window, ath match if accessToken supplied, nonce match if supplied, jti replay defense via `b.nonceStore`, jwk-thumbprint match if `expectedThumbprint` supplied. Returns `{ header, payload, jkt }` where `jkt` is the RFC 7638 thumbprint operators compare against the `cnf.jkt` claim of the bound access token."
1424
+ },
1425
+ {
1426
+ "title": "`b.middleware.dpop({ replayStore, algorithms, getAccessToken, getNonce, getHtu, audit })`",
1427
+ "body": "wraps verify into the request lifecycle — reads `req.headers.dpop`, reconstructs htu from `X-Forwarded-{Proto,Host}` + `req.url`, attaches `req.dpop` on success, returns 401 with `WWW-Authenticate: DPoP error=\"invalid_dpop_proof\"` on failure (or `error=\"use_dpop_nonce\"` per RFC 9449 §8 when nonce missing/mismatched). Default algorithm allowlist: ES256/384/512, PS256/384/512, RS256/384/512, EdDSA, ML-DSA-87 (forward-PQC). SLH-DSA-SHAKE-256f is intentionally omitted because Node lacks SLH-DSA JWK round-trip and the alg's ~50 KB signatures + ~80x sign-time penalty make it a poor fit for per-request proofs regardless. JWK header refuses any private-key components (`d`/`p`/`q`/`dp`/`dq`/`qi`/`k`/`priv`) — a proof must NEVER carry the private half. `crit` header rejected outright. The `replayStore` opt accepts the same atomic `checkAndInsert(jti, expireAtMs)` shape as `b.auth.jwt.verify`'s `replayStore` so the same `b.nonceStore.create()` instance can serve both primitives."
1428
+ }
1429
+ ]
1430
+ }
1431
+ ],
1432
+ "references": [
1433
+ {
1434
+ "label": "RFC 9449",
1435
+ "url": "https://www.rfc-editor.org/rfc/rfc9449.html"
1436
+ },
1437
+ {
1438
+ "label": "RFC 7638",
1439
+ "url": "https://www.rfc-editor.org/rfc/rfc7638.html"
1440
+ }
1441
+ ]
1442
+ },
1443
+ {
1444
+ "version": "0.7.69",
1445
+ "date": "2026-05-06",
1446
+ "headline": "`b.auth.jwt.verify({ replayStore })` — RFC 7519 §4.1.7 jti replay defense. Captured bearer tokens",
1447
+ "summary": "replayed against the verifier (TLS-terminated proxy capture, log scraping, browser-history exposure, leaked Authorization headers in shared dev tools) are refused with `auth-jwt/replay` when an operator wires the new `replayStore` opt.",
1448
+ "sections": [
1449
+ {
1450
+ "heading": "Fixed",
1451
+ "items": [
1452
+ {
1453
+ "title": "`b.auth.jwt.verify({ replayStore })` — RFC 7519 §4.1.7 jti replay defense. Captured bearer tokens",
1454
+ "body": "`b.auth.jwt.verify({ replayStore })` — RFC 7519 §4.1.7 jti replay defense. Captured bearer tokens replayed against the verifier (TLS-terminated proxy capture, log scraping, browser-history exposure, leaked Authorization headers in shared dev tools) are refused with `auth-jwt/replay` when an operator wires the new `replayStore` opt. The store contract is the same atomic `checkAndInsert(jti, expireAtMs)` that `b.nonceStore` exposes — first call records the jti, any later call with the same jti returns false and the verifier throws. The token MUST carry a non-empty `jti` claim; without one the verifier throws `auth-jwt/replay-no-jti` rather than silently letting every jti-less token through. Bad-shape stores throw `auth-jwt/bad-replay-store` at config-mistake time; backend-level errors propagate as `auth-jwt/replay-store-failed`. The TTL is bounded by the token's `exp` claim, capped at 24h so an in-memory backend can't be made to grow unbounded by tokens with absurd exp claims. Use `b.nonceStore.create({ backend: \"memory\" })` for single-node, `{ backend: \"cluster\" }` for the framework's external-DB cluster posture, or supply a Redis/Memcached/etc. backend by passing any object with a compatible `checkAndInsert` method. Replay defense remains opt-in — the verifier's default behavior is unchanged."
1455
+ }
1456
+ ]
1457
+ }
1458
+ ],
1459
+ "references": [
1460
+ {
1461
+ "label": "RFC 7519",
1462
+ "url": "https://www.rfc-editor.org/rfc/rfc7519.html"
1463
+ }
1464
+ ]
1465
+ },
1466
+ {
1467
+ "version": "0.7.68",
1468
+ "date": "2026-05-06",
1469
+ "headline": "`b.network.dns.resolveSecure(host, type)` + DNS name-compression parser bug fix. The pre-0.7.68",
1470
+ "summary": "`_decodeDnsAnswer` had a name-walk bug: after a name-compression pointer (RFC 1035 §3.1 — high two bits `11`), the parser unconditionally executed `if (buf[off] === 0) off++` which consumed the high byte of the next field.",
1471
+ "sections": [
1472
+ {
1473
+ "heading": "Fixed",
1474
+ "items": [
1475
+ {
1476
+ "title": "`b.network.dns.resolveSecure(host, type)`",
1477
+ "body": "is the new DNSSEC-aware resolution API. Returns `{ rrs, ad }` where `ad` is the AD bit (RFC 4035) set by the upstream DoH resolver after chain validation, and `rrs` is the answer-record list. Only available over DoH (`useDnsOverHttps`) — DoT and the system resolver don't surface the AD bit through Node's API today. Operators wiring DANE / TLSA validation (RFC 7672 §1.3) refuse the chain when `ad === false`. The `network-smtp-policy.js` docstring updates to point operators at this primitive. `_readAdBit(buf)` helper extracts the AD bit (byte 3, mask 0x20) from any DNS reply."
1478
+ }
1479
+ ]
1480
+ }
1481
+ ],
1482
+ "references": [
1483
+ {
1484
+ "label": "RFC 1035",
1485
+ "url": "https://www.rfc-editor.org/rfc/rfc1035.html"
1486
+ },
1487
+ {
1488
+ "label": "RFC 4035",
1489
+ "url": "https://www.rfc-editor.org/rfc/rfc4035.html"
1490
+ },
1491
+ {
1492
+ "label": "RFC 7672",
1493
+ "url": "https://www.rfc-editor.org/rfc/rfc7672.html"
1494
+ }
1495
+ ]
1496
+ },
1497
+ {
1498
+ "version": "0.7.67",
1499
+ "date": "2026-05-06",
1500
+ "headline": "`b.middleware.gpc` (Sec-GPC honoring) + `Reporting-Endpoints` opt on",
1501
+ "summary": "`b.middleware.securityHeaders`. `b.middleware.gpc({ audit, consent, mode, statusHeader })` reads the `Sec-GPC: 1` request header (W3C Privacy Sandbox / IETF draft-doty-gpc-header) and sets `req.gpcOptOut = true` for downstream consumers.",
1502
+ "sections": [
1503
+ {
1504
+ "heading": "Added",
1505
+ "items": [
1506
+ {
1507
+ "title": "`b.middleware.gpc({ audit, consent, mode, statusHeader })`",
1508
+ "body": "reads the `Sec-GPC: 1` request header (W3C Privacy Sandbox / IETF draft-doty-gpc-header) and sets `req.gpcOptOut = true` for downstream consumers. Optional `consent` integration calls `consent.recordOptOut({ req, purposes, source: \"sec-gpc\", mode })` so the operator's data-flow primitives can refuse `sale` / `share` / `targeted-ads` / `cross-context-behavioral-advertising` / `profiling` purposes for the session. Echoes a `Sec-GPC-Status: honored` response header so the UA + audit logs see the acknowledgement."
1509
+ },
1510
+ {
1511
+ "title": "Compliance context",
1512
+ "body": "Sec-GPC is legally required by California (CCPA/CPRA) since Jan 2024 and by 12+ US states (Colorado, Connecticut, Texas, Oregon, Delaware, Montana, Iowa, Nebraska, New Hampshire, New Jersey, Maryland, Minnesota) by various dates through Jan 2026. CPPA fines up to $7,500 per intentional violation."
1513
+ },
1514
+ {
1515
+ "title": "`b.middleware.securityHeaders({ reportingEndpoints: { default: \"https://...\", csp:",
1516
+ "body": "when operator passes a map of endpoint-name → URL, emits `Reporting-Endpoints: name=\"url\", ...` (W3C Reporting API). When the `default` endpoint is set AND the operator hasn't overridden the framework's default CSP, auto-appends `report-to default` to the CSP so violations route to the named endpoint."
1517
+ }
1518
+ ]
1519
+ }
1520
+ ]
1521
+ },
1522
+ {
1523
+ "version": "0.7.66",
1524
+ "date": "2026-05-06",
1525
+ "headline": "`b.mail.unsubscribe` — RFC 8058 / RFC 2369 List-Unsubscribe support. Two pieces ship together:",
1526
+ "summary": "`b.mail.unsubscribe.buildHeaders({ url, mailto, oneClick })` produces the `List-Unsubscribe` and (when `oneClick: true`) `List-Unsubscribe-Post: List-Unsubscribe=One-Click` header values.",
1527
+ "sections": [
1528
+ {
1529
+ "heading": "Added",
1530
+ "items": [
1531
+ {
1532
+ "title": "`b.mail.unsubscribe.buildHeaders({ url, mailto, oneClick })`",
1533
+ "body": "produces the `List-Unsubscribe` and (when `oneClick: true`) `List-Unsubscribe-Post: List-Unsubscribe=One-Click` header values."
1534
+ },
1535
+ {
1536
+ "title": "`b.mail.unsubscribe.handler({ onUnsubscribe })`",
1537
+ "body": "is the request-lifecycle middleware that validates the RFC 8058 §3.1 one-click POST body (`List-Unsubscribe=One-Click` exact byte sequence) and dispatches to the operator's `onUnsubscribe` callback; on success returns 200 OK with empty body. Refuses non-POST (405) / wrong body (400) / oversized body (413). Compliance context: Gmail + Yahoo bulk-sender requirements (Feb 2024) mandate one-click List-Unsubscribe for senders >= 5k/day; Microsoft 365 followed in 2025."
1538
+ },
1539
+ {
1540
+ "title": "`b.mail.send({ unsubscribe: { ... } })`",
1541
+ "body": "is the convenience opt — the existing `send` translates the structured unsubscribe value into the right header pair before transport. Operators using their own header-construction continue to work unchanged."
1542
+ }
1543
+ ]
1544
+ }
1545
+ ],
1546
+ "references": [
1547
+ {
1548
+ "label": "RFC 8058",
1549
+ "url": "https://www.rfc-editor.org/rfc/rfc8058.html"
1550
+ },
1551
+ {
1552
+ "label": "RFC 2369",
1553
+ "url": "https://www.rfc-editor.org/rfc/rfc2369.html"
1554
+ }
1555
+ ]
1556
+ },
1557
+ {
1558
+ "version": "0.7.65",
1559
+ "date": "2026-05-06",
1560
+ "headline": "Vendor manifest tamper defense + JWT keyResolver kid contract",
1561
+ "summary": "`lib/vendor/MANIFEST.json` gains SHA-256 hashes per file. Each vendored package's `files` map now has a corresponding `hashes` map (`sha256:...` for files, `sha256-tree:...` for directory trees).",
1562
+ "sections": [
1563
+ {
1564
+ "heading": "Fixed",
1565
+ "items": [
1566
+ {
1567
+ "title": "`lib/vendor/MANIFEST.json` gains SHA-256 hashes per file",
1568
+ "body": "Each vendored package's `files` map now has a corresponding `hashes` map (`sha256:...` for files, `sha256-tree:...` for directory trees). Closes the supply-chain class where a compromised `scripts/vendor-update.sh` could swap a vendored dependency silently — the on-disk content must match the committed hash. Verification gate ships in `test/layer-0-primitives/vendor-manifest.test.js` so smoke catches drift locally before commit."
1569
+ },
1570
+ {
1571
+ "title": "`scripts/refresh-vendor-manifest.js`",
1572
+ "body": "is the operator-facing tool — run it after `scripts/vendor-update.sh` bumps a vendored package to update the manifest."
1573
+ },
1574
+ {
1575
+ "title": "`b.auth.jwt` `keyResolver` contract",
1576
+ "body": "the docstring above the resolver call site now points operators at `b.guardJwt.kidSafe(header.kid)` for path-traversal sanitization. The `kidSafe` helper has shipped since v0.7.50; this slice surfaces the contract directly in the JWT verifier so operators wiring custom resolvers (cache lookup, file load, JWKS index) can't miss it."
1577
+ }
1578
+ ]
1579
+ }
1580
+ ]
1581
+ },
1582
+ {
1583
+ "version": "0.7.64",
1584
+ "date": "2026-05-06",
1585
+ "headline": "HTTP/2 + WebSocket DoS hardening",
1586
+ "summary": "HTTP/2 server caps — `lib/router.js` `http2.createSecureServer` now ships framework-default hardening: `maxConcurrentStreams: 100` (CVE-2023-44487 Rapid Reset cap; Node default was 4294967295), `maxSessionMemory: 10` (MB), `maxHeaderListPairs: 100` (CVE-2024-27983 / CVE-2024-.",
1587
+ "sections": [
1588
+ {
1589
+ "heading": "Fixed",
1590
+ "items": [
1591
+ {
1592
+ "title": "HTTP/2 server caps",
1593
+ "body": "`lib/router.js` `http2.createSecureServer` now ships framework-default hardening: `maxConcurrentStreams: 100` (CVE-2023-44487 Rapid Reset cap; Node default was 4294967295), `maxSessionMemory: 10` (MB), `maxHeaderListPairs: 100` (CVE-2024-27983 / CVE-2024-28182 CONTINUATION-flood cap), `maxSettings: 32`, `peerMaxConcurrentStreams: 100`, `unknownProtocolTimeout: 10s` (Slowloris-h2 variant). Operator-supplied `tlsOptions` override any of these."
1594
+ },
1595
+ {
1596
+ "title": "Slowloris timeouts",
1597
+ "body": "`server.headersTimeout = 60s`, `server.requestTimeout = 5min`, `server.keepAliveTimeout = 5s` set explicitly post-listen. The framework was previously relying on Node-version-shifting defaults; pinning brings older Node releases up to the modern bar."
1598
+ },
1599
+ {
1600
+ "title": "WebSocket origin default",
1601
+ "body": "*breaking change* (pre-1.0, no compat shim — operators upgrade across breaking changes): when `origins` is omitted from `b.websocket.create({ ... })`, the new default is same-origin enforcement (Origin header host must match Host header). Pre-0.7.64 the default was \"accept all\", which is the canonical Cross-Site WebSocket Hijacking (CSWSH) class. Operators needing cross-origin opt in via `origins: \"*\"` (with audited reason) or `origins: [...allowlist]`. Non-browser clients (no Origin header) continue to bypass — origin enforcement is a browser-class defense."
1602
+ }
1603
+ ]
1604
+ }
1605
+ ],
1606
+ "references": [
1607
+ {
1608
+ "label": "CVE-2023-44487",
1609
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-44487"
1610
+ },
1611
+ {
1612
+ "label": "CVE-2024-27983",
1613
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-27983"
1614
+ },
1615
+ {
1616
+ "label": "CVE-2024-28182",
1617
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-28182"
1618
+ }
1619
+ ]
1620
+ },
1621
+ {
1622
+ "version": "0.7.63",
1623
+ "date": "2026-05-06",
1624
+ "headline": "Gitleaks regex allowlist extended to cover JWT fixtures split across multiple string literals",
1625
+ "summary": "The v0.7.62 regex only matched the full three-segment JWT compact-serialization shape; source files split long JWT fixtures across literals for line-length, so gitleaks saw individual `eyJ...`-prefixed base64url segments and still flagged them. Added a second allowlist regex matching any `eyJ`-prefixed segment of substantive length (`{20,}`).",
1626
+ "sections": [
1627
+ {
1628
+ "heading": "Added",
1629
+ "items": [
1630
+ {
1631
+ "title": "Gitleaks regex allowlist extended to cover JWT fixtures split across multiple string literals",
1632
+ "body": "gitleaks regex allowlist extended to cover JWT fixtures split across multiple string literals. The v0.7.62 regex only matched the full three-segment JWT compact-serialization shape; source files split long JWT fixtures across literals for line-length, so gitleaks saw individual `eyJ...`-prefixed base64url segments and still flagged them. Added a second allowlist regex matching any `eyJ`-prefixed segment of substantive length (`{20,}`). Same rationale: real signing keys never appear as `eyJ...` base64url tokens — they're PEM / DER / PKCS#8."
1633
+ }
1634
+ ]
1635
+ }
1636
+ ]
1637
+ },
1638
+ {
1639
+ "version": "0.7.62",
1640
+ "date": "2026-05-06",
1641
+ "headline": "Gitleaks regex allowlist for JWT compact-serialization shape",
1642
+ "summary": "The new `b.guardJwt` and `b.guardAuth` test fixtures legitimately embed JWT-shaped strings as benign + hostile inputs; gitleaks' default `generic-api-key` rule fires on the high-entropy base64url segments and refuses every release tag with the fixtures present.",
1643
+ "sections": [
1644
+ {
1645
+ "heading": "Added",
1646
+ "items": [
1647
+ {
1648
+ "title": "Gitleaks regex allowlist for JWT compact-serialization shape (`eyJ",
1649
+ "body": "gitleaks regex allowlist for JWT compact-serialization shape (`eyJ...header.eyJ...payload.signature`). The new `b.guardJwt` and `b.guardAuth` test fixtures legitimately embed JWT-shaped strings as benign + hostile inputs; gitleaks' default `generic-api-key` rule fires on the high-entropy base64url segments and refuses every release tag with the fixtures present. Real signing keys never appear in compact serialization shape — they're PEM / DER / PKCS#8 — so this allowlist doesn't suppress detection of actual key leaks. Allowlist regex added under the existing \"Doc-string credential-shaped placeholders\" block in `.gitleaks.toml`. No code change."
1650
+ }
1651
+ ]
1652
+ }
1653
+ ]
1654
+ },
1655
+ {
1656
+ "version": "0.7.61",
1657
+ "date": "2026-05-06",
1658
+ "headline": "Eslint cleanup in `lib/guard-regex.js` and `lib/guard-shell.js`",
1659
+ "summary": "The `no-useless-escape` rule (eslint v9+) flagged unnecessary backslashes inside regex character classes — `*`, `+`, `?`, `[` don't need escaping when they appear inside `[...]`. Behavior unchanged: regex semantics are identical with or without the escapes (the engine treats both forms as the literal character).",
1660
+ "sections": [
1661
+ {
1662
+ "heading": "Added",
1663
+ "items": [
1664
+ {
1665
+ "title": "Eslint cleanup in `lib/guard-regex",
1666
+ "body": "eslint cleanup in `lib/guard-regex.js` and `lib/guard-shell.js`. The `no-useless-escape` rule (eslint v9+) flagged unnecessary backslashes inside regex character classes — `*`, `+`, `?`, `[` don't need escaping when they appear inside `[...]`. Behavior unchanged: regex semantics are identical with or without the escapes (the engine treats both forms as the literal character). The framework's CI gate runs eslint with `--max-warnings 0`; this slice unblocks the CI lint job that's been failing on tag pushes since v0.7.53. No operator-facing behavior change."
1667
+ }
1668
+ ]
1669
+ }
1670
+ ]
1671
+ },
1672
+ {
1673
+ "version": "0.7.60",
1674
+ "date": "2026-05-05",
1675
+ "headline": "`b.crypto.encryptEnvelopeAsCertPeer` + `b.crypto.decryptEnvelopeAsCertPeer` — cert-bound envelope",
1676
+ "summary": "primitives. The default `b.crypto.encrypt` / `b.crypto.decrypt` source the recipient from a published framework keypair (operator owns both halves); the new cert-peer variants source the recipient's ECDH P-384 half from a TLS peer cert plus a peer-supplied ML-KEM-1024 pubkey.",
1677
+ "sections": [
1678
+ {
1679
+ "heading": "Added",
1680
+ "items": [
1681
+ {
1682
+ "title": "`b.crypto.encryptEnvelopeAsCertPeer` + `b.crypto.decryptEnvelopeAsCertPeer` — cert-bound envelope",
1683
+ "body": "`b.crypto.encryptEnvelopeAsCertPeer` + `b.crypto.decryptEnvelopeAsCertPeer` — cert-bound envelope primitives. The default `b.crypto.encrypt` / `b.crypto.decrypt` source the recipient from a published framework keypair (operator owns both halves); the new cert-peer variants source the recipient's ECDH P-384 half from a TLS peer cert plus a peer-supplied ML-KEM-1024 pubkey. Wire format unchanged — the envelope dispatches on the same version bytes and KEM ID; only the input keys differ. Use cases: sealed-storage records with peer recipients (operator A seals to operator B's TLS cert + KEM pubkey), cross-service messages between cert-identified peers without a shared framework keypair, audit log entries tagged with peer recipients. The encrypt path extracts the cert's SPKI as P-384 ECDH pubkey and refuses with `crypto/cert-key-not-ecdh-p384` if the cert isn't `id-ecPublicKey` over `secp384r1`; the decrypt path accepts either a `KeyObject` or a PEM string for `certPrivateKey` and applies the same curve check. Math is the existing hybrid ML-KEM-1024 + P-384 ECDH + SHAKE256 + XChaCha20-Poly1305 — these are convenience wrappers, not new crypto."
1684
+ }
1685
+ ]
1686
+ }
1687
+ ]
1688
+ },
1689
+ {
1690
+ "version": "0.7.59",
1691
+ "date": "2026-05-05",
1692
+ "headline": "`b.guardAuth` — composite auth-bundle safety primitive (KIND=\"auth-bundle\"). Composes `guardJwt` +",
1693
+ "summary": "`guardOauth` + `b.cookies.parseSafe` + light header-smuggling detection into a single auth-flow gate. Consumes `ctx.authBundle` shape `{ jwtToken?, oauthFlow?, cookieHeader?, requestHeaders? }`.",
1694
+ "sections": [
1695
+ {
1696
+ "heading": "Added",
1697
+ "items": [
1698
+ {
1699
+ "title": "`b.guardAuth` — composite auth-bundle safety primitive (KIND=\"auth-bundle\"). Composes `guardJwt` +",
1700
+ "body": "`b.guardAuth` — composite auth-bundle safety primitive (KIND=\"auth-bundle\"). Composes `guardJwt` + `guardOauth` + `b.cookies.parseSafe` + light header-smuggling detection into a single auth-flow gate. Consumes `ctx.authBundle` shape `{ jwtToken?, oauthFlow?, cookieHeader?, requestHeaders? }`. Each sub-validator runs independently; aggregated issues carry a `source` field (`\"jwt\"` / `\"oauth\"` / `\"cookies\"` / `\"headers\"` / `\"auth\"`) so operators see which sub-guard raised which issue. Sub-guards run at the operator's chosen `childProfile` (default tracks the auth profile). `requireAtLeastOne: true` (strict default) refuses empty bundles to defend against the operator-misuse class where the gate runs but no auth input is supplied. Profiles: `strict` (childProfile=strict, requireAtLeastOne), `balanced` (childProfile=balanced, allow empty), `permissive` (childProfile=permissive, allow empty). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Adaptive integration harness gains a KIND=\"auth-bundle\" dispatcher reading `ctx.authBundle`. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1701
+ }
1702
+ ]
1703
+ }
1704
+ ]
1705
+ },
1706
+ {
1707
+ "version": "0.7.58",
1708
+ "date": "2026-05-05",
1709
+ "headline": "`b.guardPdf` — PDF identifier-safety primitive (KIND=\"metadata\"). Validates PDF inputs without",
1710
+ "summary": "vendoring a full parser; operators bring their own PDF library (pdf-lib, pdfjs-dist, vendored mupdf) and feed structural metadata to the guard for policy enforcement.",
1711
+ "sections": [
1712
+ {
1713
+ "heading": "Added",
1714
+ "items": [
1715
+ {
1716
+ "title": "`b.guardPdf.inspectMagic(bytes)`",
1717
+ "body": "is the operator helper that returns `true` when bytes start with `%PDF-`. Profiles: `strict` (max 500 pages, 0 embedded files, 64 MiB), `balanced` (max 5000 pages, 10 embedded files, 128 MiB; audit non-RCE classes), `permissive` (max 50000 pages, 100 embedded files, 512 MiB; allow encrypted). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Auto-registers into `b.guardAll` as a STANDALONE_GUARD. set of the original guard-family expansion plan complete."
1718
+ }
1719
+ ]
1720
+ }
1721
+ ]
1722
+ },
1723
+ {
1724
+ "version": "0.7.57",
1725
+ "date": "2026-05-05",
1726
+ "headline": "`b.guardImage` — image-format identifier-safety primitive (KIND=\"metadata\"). Validates image-format",
1727
+ "summary": "inputs without vendoring a full decoder; the framework's stance is operators bring their own decoder (sharp, jimp, libvips wrappers, etc.) and `guardImage` closes the magic-byte vs declared-Content-Type mismatch class plus operator-supplied metadata bounds.",
1728
+ "sections": [
1729
+ {
1730
+ "heading": "Added",
1731
+ "items": [
1732
+ {
1733
+ "title": "`b.guardImage.inspectMagic(bytes)`",
1734
+ "body": "is the operator helper that returns the detected MIME types without running the full validate / gate flow. Profiles: `strict` (8192×8192, 60 frames, 32 MiB), `balanced` (16384×16384, 200 frames, 64 MiB), `permissive` (65536×65536, 1000 frames, 256 MiB). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Adaptive integration harness gains a KIND=\"metadata\" dispatcher reading `ctx.metadata`. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1735
+ }
1736
+ ]
1737
+ }
1738
+ ]
1739
+ },
1740
+ {
1741
+ "version": "0.7.56",
1742
+ "date": "2026-05-05",
1743
+ "headline": "`b.guardTemplate` — Server-Side Template Injection (SSTI) identifier-safety primitive",
1744
+ "summary": "(KIND=\"identifier\"). Detects template-engine syntax in user-input strings before they're rendered through any template engine; refused by default at every profile because operator-untrusted input rarely legitimately contains template syntax.",
1745
+ "sections": [
1746
+ {
1747
+ "heading": "Added",
1748
+ "items": [
1749
+ {
1750
+ "title": "`b.guardTemplate` — Server-Side Template Injection (SSTI) identifier-safety primitive",
1751
+ "body": "`b.guardTemplate` — Server-Side Template Injection (SSTI) identifier-safety primitive (KIND=\"identifier\"). Detects template-engine syntax in user-input strings before they're rendered through any template engine; refused by default at every profile because operator-untrusted input rarely legitimately contains template syntax. Threat catalog: Jinja / Django / Twig / Liquid / Handlebars / AngularJS `{{...}}` + `{%...%}` (CVE-2024-22195 Jinja `xml_attr` filter, CVE-2024-26139 Bottle, CVE-2024-23348 Pyrogram); ERB / Tornado `<%...%>` and `<%=...%>`; Pug `#{...}` + `!{...}` interpolation; Mako / Velocity / Tornado `${...}` interpolation; Velocity directives (`#set` / `#if` / `#else` / `#elseif` / `#end` / `#foreach` / `#parse` / `#include` / `#stop`); BIDI / null / control / zero-width universal refuse. Profiles: `strict` (refuse everything), `balanced` (Jinja / ERB / Pug / Velocity directives still refused; audit `${...}` since it can also be a JS template-literal in some operator contexts), `permissive` (universal SSTI shapes still refused; audit `${...}` + Velocity directives). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Auto-registers into `b.guardAll` as a STANDALONE_GUARD. set of the original guard-family expansion plan complete."
1752
+ }
1753
+ ]
1754
+ }
1755
+ ],
1756
+ "references": [
1757
+ {
1758
+ "label": "CVE-2024-22195",
1759
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-22195"
1760
+ },
1761
+ {
1762
+ "label": "CVE-2024-26139",
1763
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-26139"
1764
+ },
1765
+ {
1766
+ "label": "CVE-2024-23348",
1767
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-23348"
1768
+ }
1769
+ ]
1770
+ },
1771
+ {
1772
+ "version": "0.7.55",
1773
+ "date": "2026-05-05",
1774
+ "headline": "`b.guardJsonpath` — JSONPath identifier-safety primitive (KIND=\"identifier\"). Validates",
1775
+ "summary": "user-supplied JSONPath strings (RFC 9535) before they're handed to a JSONPath evaluator. Many JSONPath libraries (notably the original Stefan Goessner implementation and several JS forks) route filter / script expressions through dynamic-code execution, turning a query path into an RCE primitive.",
1776
+ "sections": [
1777
+ {
1778
+ "heading": "Added",
1779
+ "items": [
1780
+ {
1781
+ "title": "`b.guardJsonpath` — JSONPath identifier-safety primitive (KIND=\"identifier\"). Validates",
1782
+ "body": "`b.guardJsonpath` — JSONPath identifier-safety primitive (KIND=\"identifier\"). Validates user-supplied JSONPath strings (RFC 9535) before they're handed to a JSONPath evaluator. Many JSONPath libraries (notably the original Stefan Goessner implementation and several JS forks) route filter / script expressions through dynamic-code execution, turning a query path into an RCE primitive. Threat catalog: filter expression `?(...)` (dynamic-code-execution class — universally refused); script expression `(@.x)` style; JS-source hint detection (path containing tokens that only appear in code-injection attempts — dynamic-code-exec keyword, constructor invocation keyword, function-declaration keyword, arrow-function arrow, statement-separator semicolon — all built from explicit substrings to keep the source file free of the literal keywords); recursive-descent depth bombs (operator-tunable `maxRecursiveDescents`); excessive bracket nesting; oversized pattern; BIDI / null / control / zero-width universal refuse. Profiles: `strict` (refuse everything), `balanced` (RCE class refused; audit bracket nesting + recursive descent), `permissive` (RCE class still refused; allow recursive descent up to 16). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1783
+ }
1784
+ ]
1785
+ }
1786
+ ],
1787
+ "references": [
1788
+ {
1789
+ "label": "RFC 9535",
1790
+ "url": "https://www.rfc-editor.org/rfc/rfc9535.html"
1791
+ }
1792
+ ]
1793
+ },
1794
+ {
1795
+ "version": "0.7.54",
1796
+ "date": "2026-05-05",
1797
+ "headline": "`b.guardRegex` — regex pattern identifier-safety primitive (KIND=\"identifier\"). Validates",
1798
+ "summary": "user-supplied regex pattern strings for catastrophic-backtracking (ReDoS) shapes BEFORE compilation.",
1799
+ "sections": [
1800
+ {
1801
+ "heading": "Added",
1802
+ "items": [
1803
+ {
1804
+ "title": "`b.guardRegex` — regex pattern identifier-safety primitive (KIND=\"identifier\"). Validates",
1805
+ "body": "`b.guardRegex` — regex pattern identifier-safety primitive (KIND=\"identifier\"). Validates user-supplied regex pattern strings for catastrophic-backtracking (ReDoS) shapes BEFORE compilation. Threat catalog: nested quantifiers (canonical ReDoS class — `(a+)+`, `(a*)+`, `(.+)+` — CVE-2024-21538 cross-spawn / CVE-2022-25929 chartjs-adapter-luxon are recent prominent examples; universally refused at every profile); alternation with quantifier (`(a|b)+`); bounded-repeat upper-bound overflow (operator-tunable `maxBoundedRepeat` — strict 100 / balanced 1000 / permissive 10000); lookaround with internal quantifier; oversized pattern; BIDI / null / control / zero-width universal refuse. Profiles: `strict` (refuse everything), `balanced` (nested quants still refused; audit the rest), `permissive` (nested quants + codepoint class still refused; allow alternation-quant). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1806
+ }
1807
+ ]
1808
+ }
1809
+ ],
1810
+ "references": [
1811
+ {
1812
+ "label": "CVE-2024-21538",
1813
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-21538"
1814
+ },
1815
+ {
1816
+ "label": "CVE-2022-25929",
1817
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-25929"
1818
+ }
1819
+ ]
1820
+ },
1821
+ {
1822
+ "version": "0.7.53",
1823
+ "date": "2026-05-05",
1824
+ "headline": "`b.guardShell` — shell-argument identifier-safety primitive (KIND=\"identifier\"). Validates",
1825
+ "summary": "user-input strings BEFORE they're handed to a child-process spawn. The canonical defense remains \"use array args + `shell: false`\", but operators still receive operator-untrusted strings that flow through path-arg or arg-list shapes — `guardShell` refuses obvious shell-injection shapes before the spawn call.",
1826
+ "sections": [
1827
+ {
1828
+ "heading": "Added",
1829
+ "items": [
1830
+ {
1831
+ "title": "`b.guardShell` — shell-argument identifier-safety primitive (KIND=\"identifier\"). Validates",
1832
+ "body": "`b.guardShell` — shell-argument identifier-safety primitive (KIND=\"identifier\"). Validates user-input strings BEFORE they're handed to a child-process spawn. The canonical defense remains \"use array args + `shell: false`\", but operators still receive operator-untrusted strings that flow through path-arg or arg-list shapes — `guardShell` refuses obvious shell-injection shapes before the spawn call. Threat catalog: POSIX shell metacharacters (`;`, `&`, `|`, `<`, `>`, `(`, `)`, `{`, `}`, `[`, `]`, `*`, `?`, `~`, `!`, `#`, `\\`, single+double quotes), cmd.exe metacharacters (`&`, `|`, `<`, `>`, `^`, `%`, `\"`, `'`, `(`, `)`, `,`, `;`, `=`), `$(...)` + `${VAR}` command + parameter substitution, backtick command substitution, `<(...)` / `>(...)` Bash process substitution, `$VAR` parameter expansion, newline / NUL injection (line-splitting class), leading-hyphen option-flag injection (operator opt-in to refuse args starting with `-` — defends against `-rf` / `--exec` shapes interpreted as option flags), BIDI / null / control / zero-width universal refuse. Profiles: `strict` (refuse everything), `balanced` (universal-refuse class only — substitutions / newline / BIDI / null still refused; metacharacters audited), `permissive` (universal-refuse class still refused; metacharacters audited; leading hyphen allowed). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1833
+ }
1834
+ ]
1835
+ }
1836
+ ]
1837
+ },
1838
+ {
1839
+ "version": "0.7.52",
1840
+ "date": "2026-05-05",
1841
+ "headline": "`b.guardGraphql` — GraphQL request-shape safety primitive (KIND=\"graphql-request\"). Validates",
1842
+ "summary": "user-supplied GraphQL request bundles against the canonical query-shape DoS catalog before the framework hands the query to a schema-aware executor.",
1843
+ "sections": [
1844
+ {
1845
+ "heading": "Added",
1846
+ "items": [
1847
+ {
1848
+ "title": "`b.guardGraphql` — GraphQL request-shape safety primitive (KIND=\"graphql-request\"). Validates",
1849
+ "body": "`b.guardGraphql` — GraphQL request-shape safety primitive (KIND=\"graphql-request\"). Validates user-supplied GraphQL request bundles against the canonical query-shape DoS catalog before the framework hands the query to a schema-aware executor. Threat catalog: query depth bombs (N² query-shape DoS — caps at strict 8 / balanced 12 / permissive 24); alias-bomb breadth DoS (caps at 8/16/32 aliases per selection-set); introspection in production (`__schema` / `__type` schema-leak); batch query DoS (caps at 1/10/50 entries per array); persisted-query enforcement (refuse free-form queries when `persistedQueryPolicy: \"require\"`); operation-name allowlist drift; variable type confusion (operator declares `variableShapes: { id: \"string\", limit: \"number\" }`); oversized query / variable / total bytes; BIDI / null / control / zero-width universal refuse on the query string. Brace-counting query-shape walker handles strings + comments correctly without requiring a full GraphQL parser. Profiles: `strict` (refuse introspection + batch + shape-DoS), `balanced` (audit most, allow batch up to 10), `permissive` (universal-refuse class still refused; rest allow / audit). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Adaptive integration harness gains a KIND=\"graphql-request\" dispatcher reading `ctx.graphqlRequest`. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1850
+ }
1851
+ ]
1852
+ }
1853
+ ]
1854
+ },
1855
+ {
1856
+ "version": "0.7.51",
1857
+ "date": "2026-05-05",
1858
+ "headline": "`b.guardOauth` — OAuth flow-shape safety primitive (KIND=\"oauth-flow\"). Validates user-supplied",
1859
+ "summary": "OAuth 2.x / OIDC authorization-code-flow parameter bundles before the framework's `b.auth.oauth` client exchanges them.",
1860
+ "sections": [
1861
+ {
1862
+ "heading": "Added",
1863
+ "items": [
1864
+ {
1865
+ "title": "`b.guardOauth` — OAuth flow-shape safety primitive (KIND=\"oauth-flow\"). Validates user-supplied",
1866
+ "body": "`b.guardOauth` — OAuth flow-shape safety primitive (KIND=\"oauth-flow\"). Validates user-supplied OAuth 2.x / OIDC authorization-code-flow parameter bundles before the framework's `b.auth.oauth` client exchanges them. Threat catalog: PKCE missing or non-S256 (RFC 7636 / OAuth 2.1 mandate; `plain` is the downgrade-attack class); state missing (RFC 6749 §10.12 CSRF); redirect_uri not in operator allowlist (exact-match per OAuth 2.1 — no prefix / wildcard / scheme drift); response_type allowlist drift (refuse implicit `token` deprecated in OAuth 2.1, require operator-allowed types); scope-token shape per RFC 6749 §3.3 (refuse non-printable / control / whitespace-other-than-space); issuer missing on callback (RFC 9207 IdP-mix-up defense — set `flow._isCallback = true` to enforce); authorization-code reuse via operator-supplied `seenCodeStore.hasSeen(code)` (RFC 6749 §10.5 replay class); excessive parameter / total bytes; BIDI / null / control / zero-width universal refuse. Profiles: `strict` (PKCE S256, state required, redirect_uri exact-match, RFC 9207 iss required), `balanced` (PKCE any method, state + redirect_uri allowlist required, iss audited), `permissive` (universal-refuse class still refused; rest audit). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Adaptive integration harness gains a KIND=\"oauth-flow\" dispatcher reading `ctx.oauthFlow`. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1867
+ }
1868
+ ]
1869
+ }
1870
+ ],
1871
+ "references": [
1872
+ {
1873
+ "label": "RFC 7636",
1874
+ "url": "https://www.rfc-editor.org/rfc/rfc7636.html"
1875
+ },
1876
+ {
1877
+ "label": "RFC 6749",
1878
+ "url": "https://www.rfc-editor.org/rfc/rfc6749.html"
1879
+ },
1880
+ {
1881
+ "label": "RFC 9207",
1882
+ "url": "https://www.rfc-editor.org/rfc/rfc9207.html"
1883
+ }
1884
+ ]
1885
+ },
1886
+ {
1887
+ "version": "0.7.50",
1888
+ "date": "2026-05-05",
1889
+ "headline": "`b.guardJwt` — JWT identifier-safety primitive (KIND=\"identifier\"). Validates user-supplied JWT",
1890
+ "summary": "compact-serialization strings against the canonical CVE-class refuse list before hand-off to a verifier — `b.guardJwt` never replaces signature verification, it reduces the input space the verifier sees.",
1891
+ "sections": [
1892
+ {
1893
+ "heading": "Added",
1894
+ "items": [
1895
+ {
1896
+ "title": "`b.guardJwt.kidSafe(kid)`",
1897
+ "body": "is the documented contract for operator `keyResolver` implementations: throws on traversal indicators or control bytes, returns the validated kid on success. Profiles: `strict` (refuse everything), `balanced` (refuse alg=none / kid-traversal / unknown-crit; audit the rest), `permissive` (universal-refuse class still refused). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Auto-registers into `b.guardAll` as a STANDALONE_GUARD."
1898
+ }
1899
+ ]
1900
+ }
1901
+ ],
1902
+ "references": [
1903
+ {
1904
+ "label": "RFC 7515",
1905
+ "url": "https://www.rfc-editor.org/rfc/rfc7515.html"
1906
+ },
1907
+ {
1908
+ "label": "CVE-2015-9235",
1909
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2015-9235"
1910
+ },
1911
+ {
1912
+ "label": "CVE-2018-0114",
1913
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-0114"
1914
+ }
1915
+ ]
1916
+ },
1917
+ {
1918
+ "version": "0.7.49",
1919
+ "date": "2026-05-05",
1920
+ "headline": "`b.middleware.headers(opts)` — inbound HTTP header threat-detection middleware. Sits at the top of",
1921
+ "summary": "the request lifecycle. Threat catalog: `header-name-shape` (header name not a valid RFC 9110 §5.1 token); `header-value-control-byte` (CR / LF / NUL inside any header value — header-injection defense in depth on top of Node's rejection); `header-count-cap` (default 100 inbound he.",
1922
+ "sections": [
1923
+ {
1924
+ "heading": "Added",
1925
+ "items": [
1926
+ {
1927
+ "title": "`b.middleware.headers(opts)` — inbound HTTP header threat-detection middleware. Sits at the top of",
1928
+ "body": "`b.middleware.headers(opts)` — inbound HTTP header threat-detection middleware. Sits at the top of the request lifecycle. Threat catalog: `header-name-shape` (header name not a valid RFC 9110 §5.1 token); `header-value-control-byte` (CR / LF / NUL inside any header value — header-injection defense in depth on top of Node's rejection); `header-count-cap` (default 100 inbound headers max); `header-value-cap` (default 8 KiB per value); `smuggling-cl-te` (RFC 9112 §6.1 — both Content-Length and Transfer-Encoding present, the canonical CL.TE / TE.CL request-smuggling vector); `smuggling-cl-multi` / `smuggling-te-multi` (multiple values for either header — proxy-desync class); `deprecated-trust-header` (X-Forwarded-For / -Proto / -Host / -Port / X-Real-IP present without operator-supplied `trustProxy: true` opt — operators should adopt RFC 7239 `Forwarded`). On `mode: \"enforce\"` + `refuseOnHigh` (default), refuses with HTTP 400 + JSON body listing detected high-severity issues; emits one audit row per issue regardless of mode. Complements the existing per-route smuggling defense in `b.middleware.bodyParser` by running the same check at the top of the chain (covers GET / HEAD requests that don't body-parse)."
1929
+ }
1930
+ ]
1931
+ }
1932
+ ],
1933
+ "references": [
1934
+ {
1935
+ "label": "RFC 9110",
1936
+ "url": "https://www.rfc-editor.org/rfc/rfc9110.html"
1937
+ },
1938
+ {
1939
+ "label": "RFC 9112",
1940
+ "url": "https://www.rfc-editor.org/rfc/rfc9112.html"
1941
+ },
1942
+ {
1943
+ "label": "RFC 7239",
1944
+ "url": "https://www.rfc-editor.org/rfc/rfc7239.html"
1945
+ }
1946
+ ]
1947
+ },
1948
+ {
1949
+ "version": "0.7.48",
1950
+ "date": "2026-05-05",
1951
+ "headline": "`b.cookies.parseSafe` + `b.middleware.cookies` inbound cookie-header threat detection",
1952
+ "summary": "Closes the inbound-detection gap on the cookie surface. `b.cookies.parse` remains lenient (last-write-wins, silent skip); `parseSafe` returns `{ jar, issues }` surfacing every detected anomaly, and the matching middleware wires it into the request lifecycle.",
1953
+ "sections": [
1954
+ {
1955
+ "heading": "Added",
1956
+ "items": [
1957
+ {
1958
+ "title": "`b.cookies.parseSafe(header, opts)`",
1959
+ "body": "Returns `{ jar, issues }` and surfaces every detected anomaly: header-cap (oversized Cookie header), header-control-byte (CR / LF / NUL injected through proxy — header-injection prelude class), pair-malformed (missing `=`), pair-empty-name, name-cap (oversized name), value-cap (oversized value), duplicate-name (cookie-tossing class — same name appearing more than once in one Cookie header indicates an attacker-set parent-domain cookie shadowing the legitimate one)."
1960
+ },
1961
+ {
1962
+ "title": "`b.middleware.cookies({ mode, audit, refuseOnHigh })`",
1963
+ "body": "Wires `parseSafe` into the request lifecycle: populates `req.cookieJar`, emits one audit row per detected issue, and refuses with HTTP 400 on any high-severity issue when `mode: \"enforce\"` (default)."
1964
+ }
1965
+ ]
1966
+ },
1967
+ {
1968
+ "heading": "Changed",
1969
+ "items": [
1970
+ {
1971
+ "title": "Existing `b.cookies` invariants unchanged",
1972
+ "body": "RFC 6265bis token grammar enforcement, `__Host-` / `__Secure-` prefix invariants, SameSite=None requires Secure, `Partitioned` / CHIPS attribute support, length caps on serialize-side all remain unchanged — this release closes the inbound-detection gap only."
1973
+ }
1974
+ ]
1975
+ }
1976
+ ],
1977
+ "references": [
1978
+ {
1979
+ "label": "RFC 6265bis — Cookies: HTTP State Management Mechanism",
1980
+ "url": "https://datatracker.ietf.org/doc/draft-ietf-httpbis-rfc6265bis/"
1981
+ },
1982
+ {
1983
+ "label": "CHIPS — Cookies Having Independent Partitioned State",
1984
+ "url": "https://github.com/privacycg/CHIPS"
1985
+ }
1986
+ ]
1987
+ },
1988
+ {
1989
+ "version": "0.7.47",
1990
+ "date": "2026-05-05",
1991
+ "headline": "`b.guardMime` RFC 6838 media-type identifier-safety primitive",
1992
+ "summary": "Validates user-supplied media type strings destined for Accept-shape comparison, content-type allowlists, and dispatch routing. Auto-registers into `b.guardAll` as a STANDALONE_GUARD.",
1993
+ "sections": [
1994
+ {
1995
+ "heading": "Added",
1996
+ "items": [
1997
+ {
1998
+ "title": "`b.guardMime` (KIND=\"identifier\") media-type validator",
1999
+ "body": "Validates media type strings against RFC 6838. `sanitize` lowercases type/subtype while preserving parameter case (multipart boundary tokens etc. are case-significant)."
2000
+ },
2001
+ {
2002
+ "title": "Media-type threat catalog",
2003
+ "body": "Shape malformation (missing `/`, bad type/subtype tokens against RFC 6838 §4.2 restricted-name grammar); parameter validation against the RFC 7231 §3.1.1.1 tchar token grammar (token-only or quoted-string per RFC 7230 §3.2.6); wildcard (`type/subtype` with `*`) outside Accept context refuse; vendor tree (`vnd.*`), personal tree (`prs.*`), and unregistered (`x.*` / `x-*`) namespace audit so operators audit those slots; BIDI / zero-width / control / null-byte universal refuse."
2004
+ },
2005
+ {
2006
+ "title": "Risky-type refuse list",
2007
+ "body": "Refuses executable + script-host content types: `application/x-msdownload`, `application/x-bat`, `application/x-msdos-program`, `application/x-sh`, `application/x-csh`, `application/x-perl`, `application/x-python`, `application/javascript`, `application/x-javascript`, `text/javascript`, `text/x-javascript`, `application/x-shockwave-flash`, `application/x-msi`."
2008
+ },
2009
+ {
2010
+ "title": "Profiles + compliance postures",
2011
+ "body": "Profiles: `strict` (refuse wildcard + risky-type, audit trees + parameters), `balanced` (audit most things, allow vendor tree), `permissive` (universal-refuse class still refused). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay."
2012
+ }
2013
+ ]
2014
+ }
2015
+ ],
2016
+ "references": [
2017
+ {
2018
+ "label": "RFC 6838 — Media Type Specifications and Registration Procedures",
2019
+ "url": "https://www.rfc-editor.org/rfc/rfc6838.html"
2020
+ },
2021
+ {
2022
+ "label": "RFC 7230 — HTTP/1.1 Message Syntax and Routing",
2023
+ "url": "https://www.rfc-editor.org/rfc/rfc7230.html"
2024
+ },
2025
+ {
2026
+ "label": "RFC 7231 — HTTP/1.1 Semantics and Content",
2027
+ "url": "https://www.rfc-editor.org/rfc/rfc7231.html"
2028
+ }
2029
+ ]
2030
+ },
2031
+ {
2032
+ "version": "0.7.46",
2033
+ "date": "2026-05-05",
2034
+ "headline": "`b.guardTime` RFC 3339 / ISO 8601 datetime identifier-safety primitive",
2035
+ "summary": "Validates user-supplied datetime strings destined for audit timestamps, scheduling, retention windows, query ranges, and cross-system event correlation. Auto-registers into `b.guardAll` as a STANDALONE_GUARD.",
2036
+ "sections": [
2037
+ {
2038
+ "heading": "Added",
2039
+ "items": [
2040
+ {
2041
+ "title": "`b.guardTime` (KIND=\"identifier\") datetime validator",
2042
+ "body": "Validates user-supplied datetime strings against the RFC 3339 §5.6 grammar / ISO 8601. `sanitize` normalises a space date/time separator to `T` and uppercases the trailing `z` UTC marker."
2043
+ },
2044
+ {
2045
+ "title": "Threat catalog for datetime inputs",
2046
+ "body": "Shape malformation against RFC 3339 §5.6; year-window overflow (default `[1970, 9999]`); naive datetime (no offset) refuse; non-UTC offset policy (strict requires `Z` / `+00:00`); leap-second `60` field policy (RFC 3339 §5.6 valid but parser-panic prone); excessive fractional precision cap (default 9 digits / nanoseconds); date-only and time-only refuse for full-datetime contexts; structural range violations (month / day-in-month / hour / minute / second); BIDI / zero-width / control / null-byte universal refuse."
2047
+ },
2048
+ {
2049
+ "title": "Profiles + compliance postures",
2050
+ "body": "Profiles: `strict` (refuse all of the above), `balanced` (refuse naive; audit non-UTC + leap + fractional + date/time-only), `permissive` (universal-refuse class still refused). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay."
2051
+ }
2052
+ ]
2053
+ }
2054
+ ],
2055
+ "references": [
2056
+ {
2057
+ "label": "RFC 3339 — Date and Time on the Internet",
2058
+ "url": "https://www.rfc-editor.org/rfc/rfc3339.html"
2059
+ },
2060
+ {
2061
+ "label": "ISO 8601",
2062
+ "url": "https://www.iso.org/iso-8601-date-and-time-format.html"
2063
+ }
2064
+ ]
2065
+ },
2066
+ {
2067
+ "version": "0.7.45",
2068
+ "date": "2026-05-05",
2069
+ "headline": "`b.guardCidr` CIDR identifier-safety primitive",
2070
+ "summary": "Validates user-supplied CIDR notation strings (IPv4 + IPv6) destined for network allowlists, ACLs, security-group rules, and tenant-boundary configuration. Auto-registers into `b.guardAll` as a STANDALONE_GUARD.",
2071
+ "sections": [
2072
+ {
2073
+ "heading": "Added",
2074
+ "items": [
2075
+ {
2076
+ "title": "`b.guardCidr` (KIND=\"identifier\") CIDR validator",
2077
+ "body": "Validates user-supplied IPv4 and IPv6 CIDR notation strings before they reach allowlists / ACLs / security-group rules / tenant-boundary configuration."
2078
+ },
2079
+ {
2080
+ "title": "CIDR threat catalog",
2081
+ "body": "Shape malformation, IPv4 octet overflow + leading-zero refuse, IPv6 zero-group ambiguity, mask out-of-range (IPv4 32 / IPv6 128 ceiling), network-address misalignment (host bits set on a non-/32 / non-/128 prefix — common typo class). BIDI / zero-width / control / null-byte universal refuse."
2082
+ },
2083
+ {
2084
+ "title": "Reserved-range membership detection",
2085
+ "body": "Covers all IPv4 RFC 1918 private blocks (`10/8`, `172.16/12`, `192.168/16`), loopback `127/8`, link-local `169.254/16`, multicast `224/4`, class-E `240/4`, RFC 5737 documentation `192.0.2/24` + `198.51.100/24` + `203.0.113/24`, benchmarking `198.18/15`, CGNAT `100.64/10`, this-network `0/8`, plus IPv6 loopback `::1`, unspecified `::/128`, ULA `fc00::/7`, link-local `fe80::/10`, multicast `ff00::/8`, documentation `2001:db8::/32`, teredo, deprecated 6to4 `2002::/16`."
2086
+ },
2087
+ {
2088
+ "title": "IPv4-mapped IPv6 dual-stack confusion",
2089
+ "body": "Detects `::ffff:0:0/96` dual-stack confusion (CVE-2021-22931 IPv6 variant). Bare-IP-without-mask policy: strict refuses, balanced audits, permissive allows."
2090
+ },
2091
+ {
2092
+ "title": "Profiles + compliance postures",
2093
+ "body": "Profiles: `strict` / `balanced` / `permissive`. Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay."
2094
+ }
2095
+ ]
2096
+ }
2097
+ ],
2098
+ "references": [
2099
+ {
2100
+ "label": "RFC 1918 — Address Allocation for Private Internets",
2101
+ "url": "https://www.rfc-editor.org/rfc/rfc1918.html"
2102
+ },
2103
+ {
2104
+ "label": "RFC 5737 — IPv4 Address Blocks Reserved for Documentation",
2105
+ "url": "https://www.rfc-editor.org/rfc/rfc5737.html"
2106
+ },
2107
+ {
2108
+ "label": "CVE-2021-22931 — Node.js IPv6 / IPv4-mapped resolution",
2109
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-22931"
2110
+ }
2111
+ ]
2112
+ },
2113
+ {
2114
+ "version": "0.7.44",
2115
+ "date": "2026-05-05",
2116
+ "headline": "`b.guardUuid` UUID identifier-safety primitive",
2117
+ "summary": "Validates user-supplied UUID strings per RFC 9562 (May 2024, obsoletes RFC 4122). Auto-registers into `b.guardAll` as a STANDALONE_GUARD; the adaptive integration harness picks it up via the KIND=\"identifier\" dispatcher.",
2118
+ "sections": [
2119
+ {
2120
+ "heading": "Added",
2121
+ "items": [
2122
+ {
2123
+ "title": "`b.guardUuid` (KIND=\"identifier\") UUID validator",
2124
+ "body": "Validates user-supplied UUID strings against RFC 9562. Recognises the four canonical forms (hyphenated 8-4-4-4-12, hyphenless 32-hex, Microsoft GUID braces `{...}`, `urn:uuid:` prefix) and refuses shape malformations. `sanitize` returns canonical lowercase hyphenated form (strips braces / urn prefix)."
2125
+ },
2126
+ {
2127
+ "title": "Threat catalog for UUID inputs",
2128
+ "body": "Refuses RFC 9562 §4.2 unassigned version digits (only 1-8 are defined); refuses non-RFC 4122 variant bits (only the `10xx` high-bits family is the canonical UUID variant); refuses nil UUID §5.9 / max UUID §5.10 sentinel-leak shapes; enforces a format policy (strict default = `hyphenated-only`); BIDI / zero-width / control / null-byte universal refuse via `lib/codepoint-class.js`."
2129
+ },
2130
+ {
2131
+ "title": "Profiles + compliance postures",
2132
+ "body": "Profiles: `strict` (hyphenated-only, refuse all sentinels and non-canonical forms), `balanced` (accept any form, audit sentinels), `permissive` (universal-refuse class still refused). Postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay."
2133
+ }
2134
+ ]
2135
+ }
2136
+ ],
2137
+ "references": [
2138
+ {
2139
+ "label": "RFC 9562 — Universally Unique IDentifiers (UUIDs)",
2140
+ "url": "https://www.rfc-editor.org/rfc/rfc9562.html"
2141
+ },
2142
+ {
2143
+ "label": "RFC 4122 (obsoleted)",
2144
+ "url": "https://www.rfc-editor.org/rfc/rfc4122.html"
2145
+ }
2146
+ ]
2147
+ },
2148
+ {
2149
+ "version": "0.7.43",
2150
+ "date": "2026-05-05",
2151
+ "headline": "`b.guardDomain` — domain-name identifier-safety primitive (KIND=\"identifier\"). Validates",
2152
+ "summary": "user-supplied DNS names destined for allowlists, redirect targets, webhook endpoints, email-domain extraction, and CORS origin checks.",
2153
+ "sections": [
2154
+ {
2155
+ "heading": "Added",
2156
+ "items": [
2157
+ {
2158
+ "title": "`b.guardDomain` — domain-name identifier-safety primitive (KIND=\"identifier\"). Validates",
2159
+ "body": "`b.guardDomain` — domain-name identifier-safety primitive (KIND=\"identifier\"). Validates user-supplied DNS names destined for allowlists, redirect targets, webhook endpoints, email-domain extraction, and CORS origin checks. Threat catalog: RFC 1035 §2.3.4 length caps (63 octets per label, 253 octets per FQDN), RFC 952 / 1123 LDH-rule violations (no leading/trailing hyphen, no `--` at positions 3-4 except `xn--`), IDN homograph mixed-script confusables (Latin / Cyrillic / Greek / Cherokee / Armenian / Han / Hiragana / Katakana / Hangul / Arabic / Hebrew range tables), BIDI / zero-width / control / null universal refuse via `lib/codepoint-class.js` (CVE-2021-42574 Trojan Source class), Punycode A-label malformation (bare `xn--`, double-encoded), RFC 6761 special-use suffix matching (`.localhost` / `.local` / `.invalid` / `.test` / `.onion` / `.alt` / `.home.arpa` / `.internal`), IPv4-as-domain confusion (CVE-2021-22931 — dotted-decimal / octal / hex / long-decimal forms), IPv6 bracket-literal, single-label / TLD-only refuse, wildcard `*` label refuse at every profile, RFC 8552 underscore-service-label policy, DGA Shannon-entropy heuristic for high-entropy long single labels (Mirai / Conficker C2 shape). Profiles: `strict` (Latin-only scripts, refuse-on-everything), `balanced` (audit Punycode, allow major international scripts, audit DGA), `permissive` (universal-refuse class still refused; everything else audit / allow). Compliance postures: `hipaa` / `pci-dss` / `soc2` strict overlay; `gdpr` balanced overlay. Auto-registers into `b.guardAll` as a STANDALONE_GUARD; the adaptive integration harness at `test/layer-5-integration/guard-host-integration.test.js` picks it up automatically. Defer-with-condition: full UTS #46 ToASCII / ToUnicode round-trip and Public-Suffix-List boundary enforcement ship behind operator-supplied callbacks (`opts.idnToAscii`, `opts.publicSuffixList`); re-open conditions are documented in the wiki page."
2160
+ }
2161
+ ]
2162
+ }
2163
+ ],
2164
+ "references": [
2165
+ {
2166
+ "label": "RFC 1035",
2167
+ "url": "https://www.rfc-editor.org/rfc/rfc1035.html"
2168
+ },
2169
+ {
2170
+ "label": "RFC 952",
2171
+ "url": "https://www.rfc-editor.org/rfc/rfc952.html"
2172
+ },
2173
+ {
2174
+ "label": "RFC 6761",
2175
+ "url": "https://www.rfc-editor.org/rfc/rfc6761.html"
2176
+ },
2177
+ {
2178
+ "label": "RFC 8552",
2179
+ "url": "https://www.rfc-editor.org/rfc/rfc8552.html"
2180
+ },
2181
+ {
2182
+ "label": "CVE-2021-42574",
2183
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-42574"
2184
+ },
2185
+ {
2186
+ "label": "CVE-2021-22931",
2187
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-22931"
2188
+ }
2189
+ ]
2190
+ },
2191
+ {
2192
+ "version": "0.7.42",
2193
+ "date": "2026-05-05",
2194
+ "headline": "Gitleaks allowlist for v0",
2195
+ "summary": "7.28 CHANGELOG doc-snippet false-positive. The v0.7.28 release-notes entry for `b.crypto.encryptMlkem768X25519` includes a JS-object-literal code example with a `privateKey: mlkemPrivateKey` field; gitleaks' default `generic-api-key` rule fires on the high-entropy content adjacent to the `privateKey:` token, blocking every release-tag CI run since v0.7.38.",
2196
+ "sections": [
2197
+ {
2198
+ "heading": "Added",
2199
+ "items": [
2200
+ {
2201
+ "title": "Gitleaks allowlist for v0",
2202
+ "body": "gitleaks allowlist for v0.7.28 CHANGELOG doc-snippet false-positive. The v0.7.28 release-notes entry for `b.crypto.encryptMlkem768X25519` includes a JS-object-literal code example with a `privateKey: mlkemPrivateKey` field; gitleaks' default `generic-api-key` rule fires on the high-entropy content adjacent to the `privateKey:` token, blocking every release-tag CI run since v0.7.38. Allowlisted by commit + fingerprint per the same pattern as the existing `74e627e` entry — surgical suppression of the documented false positive, no broader rule weakening. Working-tree-only or rule-disabling alternatives were rejected: gitleaks' `git`-history scan is the stricter gate (catches secrets that landed and were later removed) and the documented snippet contains no real secret."
2203
+ }
2204
+ ]
2205
+ }
2206
+ ]
2207
+ },
2208
+ {
2209
+ "version": "0.7.41",
2210
+ "date": "2026-05-05",
2211
+ "headline": "Wiki primitive-validator BACKLOG sweep: 50 of 101 entries pruned",
2212
+ "summary": "Audited every entry in `examples/wiki/test/validate-primitive-sections.js`'s `UNDOCUMENTED_BACKLOG` map against the actual wiki page bodies; primitives that already have signature-form headings (`b.X.Y(...)`) discovered by the validator no longer need a BACKLOG entry suppressing them.",
2213
+ "sections": [
2214
+ {
2215
+ "heading": "Added",
2216
+ "items": [
2217
+ {
2218
+ "title": "Wiki primitive-validator BACKLOG sweep: 50 of 101 entries pruned",
2219
+ "body": "wiki primitive-validator BACKLOG sweep: 50 of 101 entries pruned. Audited every entry in `examples/wiki/test/validate-primitive-sections.js`'s `UNDOCUMENTED_BACKLOG` map against the actual wiki page bodies; primitives that already have signature-form headings (`b.X.Y(...)`) discovered by the validator no longer need a BACKLOG entry suppressing them. Removed entries: `consent`, `websocketChannels`, `ssrfGuard`, `htmlBalance`, `csv`, `uuid`, `time`, `mailBounce`, `archive`, `breakGlass`, `forms`, `render`, `errorPage`, `cluster`, `safeBuffer`, `safeSql`, `safeUrl`, `retry`, `fileType`, `scheduler`, `jobs`, `backup`, `restore`, `i18n`, `cache`, `crypto`, `createApp`, `fileUpload`, `mtlsCa`, `pqcGate`, `pqcAgent`, `permissions`, `apiKey`, `webhook`, `notify`, `credentialHash`, `queue`, plus the 11-guard family (`guardEmail` through `guardAll`). The remaining 51 entries fall into three real classes: namespace-level entries with method-only headings (vault primitives, audit chain, etc.), pages structured around backend-builder patterns instead of flat methods (`objectStore`, `backupBundle`, etc.), and internal helpers that aren't part of the operator surface (`cli`, `boot`, `dev`, `protocolDispatcher`). The shrunk BACKLOG narrows the gap operators see between \"covered by the parent's wiki page\" and \"actually drift waiting to be fixed.\"."
2220
+ }
2221
+ ]
2222
+ }
2223
+ ]
2224
+ },
2225
+ {
2226
+ "version": "0.7.40",
2227
+ "date": "2026-05-05",
2228
+ "headline": "RFC 8617 ARC chain-validity hardening: duplicate-instance refusal, gap detection, hop ceiling, per-hop `cv=` rule enforcement",
2229
+ "summary": "Closes a class of chain-injection holes the prior `arcVerify` would silently accept. Duplicate instance — two `ARC-Seal` (or AMS / AAR) headers at the same `i=N` now fail with `reason: \"duplicate-instance\"` instead of silently letting the second header overwrite the first signer's record (the latter is a known forwarder-injection attack).",
2230
+ "sections": [
2231
+ {
2232
+ "heading": "Fixed",
2233
+ "items": [
2234
+ {
2235
+ "title": "Duplicate instance",
2236
+ "body": "two `ARC-Seal` (or AMS / AAR) headers at the same `i=N` now fail with `reason: \"duplicate-instance\"` instead of silently letting the second header overwrite the first signer's record (the latter is a known forwarder-injection attack)."
2237
+ },
2238
+ {
2239
+ "title": "Non-contiguous chain",
2240
+ "body": "gap detection (`i=1, i=3` with no `i=2`) now correctly fails with `reason: \"incomplete-or-non-contiguous\"`. The prior `.some()` check skipped sparse-array empty slots and let the gap through."
2241
+ },
2242
+ {
2243
+ "title": "Hop ceiling",
2244
+ "body": "RFC 8617 §5.1.2 caps the chain at 50 sets; verifier now refuses with `reason: \"too-many-hops\"`."
2245
+ },
2246
+ {
2247
+ "title": "Per-hop `cv=` rules",
2248
+ "body": "i=1 MUST be `cv=none` (no upstream chain to validate); i≥2 MUST be `cv=pass` or `cv=fail` (cv=none invalid past hop 1); a downstream `cv=pass` after an upstream `cv=fail` is invalid (a hop can't claim chain-pass when an earlier hop saw it fail). Each violation surfaces with a structured `reason` field. Result shape gains `perHopCv: [string|null,...]` (one entry per hop) and `reason: string` when `chainStatus === \"fail\"` so operators can distinguish \"signature failed\" from \"chain rules violated\" without parsing free-text errors."
2249
+ }
2250
+ ]
2251
+ }
2252
+ ],
2253
+ "references": [
2254
+ {
2255
+ "label": "RFC 8617",
2256
+ "url": "https://www.rfc-editor.org/rfc/rfc8617.html"
2257
+ }
2258
+ ]
2259
+ },
2260
+ {
2261
+ "version": "0.7.39",
2262
+ "date": "2026-05-05",
2263
+ "headline": "RFC 8954 OCSP nonce extension default-on, plus persistent-log reliability fix in the smoke runner",
2264
+ "summary": "`b.network.tls.ocsp.buildRequest(opts)` (NEW) — constructs a DER-encoded OCSPRequest for a single (`leafCertDer`, `issuerCertDer`) pair. The RFC 8954 nonce extension is on by default (security-defaults-on rule); operators talking to responders that ignore nonces opt out via `nonce: false`.",
2265
+ "sections": [
2266
+ {
2267
+ "heading": "Fixed",
2268
+ "items": [
2269
+ {
2270
+ "title": "`b.network.tls.ocsp.buildRequest(opts)`",
2271
+ "body": "(NEW) — constructs a DER-encoded OCSPRequest for a single (`leafCertDer`, `issuerCertDer`) pair. The RFC 8954 nonce extension is on by default (security-defaults-on rule); operators talking to responders that ignore nonces opt out via `nonce: false`. Default nonce length is 16 bytes (RFC 8954 §2.1 floor 1, ceiling 32 — `nonceLen` overrides). CertID hashes are SHA-1 per RFC 6960 §4.1.1 (the universally-supported algorithm; SHA-256 in OCSP requests is §4.3 optional and many responders reject). Returns `{ requestDer, nonce }`. Operators send `requestDer` to the OCSP responder URL via `b.httpClient` (Content-Type: `application/ocsp-request`) and pass the returned `nonce` Buffer to `evaluate(responseDer, { issuerPem, expectedNonce })` — defends against replay attacks where an attacker captures a \"good\" OCSP response and replays it after the cert is revoked."
2272
+ },
2273
+ {
2274
+ "title": "DER writer surface added to `lib/asn1-der.js`",
2275
+ "body": "`writeNode` / `writeSequence` / `writeOctetString` / `writeInteger` / `writeNull` / `writeOid` / `writeContextExplicit` (minimal, ~80 lines, X.690 short + long-form length encoding)."
2276
+ },
2277
+ {
2278
+ "title": "`evaluate(der, opts)` accepts `expectedNonce`",
2279
+ "body": "when supplied, the response's nonce extension MUST match (else `tls/ocsp-nonce-mismatch`). Result shape gains a `nonce` field: `\"matched\"` / `\"present-not-checked\"` / `\"n/a\"`."
2280
+ },
2281
+ {
2282
+ "title": "Smoke-runner persistent log reliability fix",
2283
+ "body": "the `.test-output/smoke.log` tee was using `fs.createWriteStream` (async); when the runner threw on a test failure the buffer didn't flush before `process.exit`, so the failure detail never reached the log. Replaced with synchronous `fs.writeSync` against an open fd — every byte hits disk before exit. Surface change for diagnosing future failures: the log now ALWAYS captures the failure stack instead of truncating mid-run."
2284
+ }
2285
+ ]
2286
+ }
2287
+ ],
2288
+ "references": [
2289
+ {
2290
+ "label": "RFC 8954",
2291
+ "url": "https://www.rfc-editor.org/rfc/rfc8954.html"
2292
+ },
2293
+ {
2294
+ "label": "RFC 6960",
2295
+ "url": "https://www.rfc-editor.org/rfc/rfc6960.html"
2296
+ }
2297
+ ]
2298
+ },
2299
+ {
2300
+ "version": "0.7.38",
2301
+ "date": "2026-05-05",
2302
+ "headline": "RFC 6698 + RFC 7672 DANE certificate-chain verification, plus smoke-runner LPT scheduling and a wiki failure-detail bugfix",
2303
+ "summary": "`b.network.smtp.dane.verifyChain(certChain, tlsaRecords, opts?)` (NEW) — walks the peer cert chain (leaf-first DER buffers — typically `sock.getPeerCertificate(true).raw`) and confirms at least one TLSA record matches per the record's usage / selector / mtype.",
2304
+ "sections": [
2305
+ {
2306
+ "heading": "Added",
2307
+ "items": [
2308
+ {
2309
+ "title": "`b.network.smtp.dane.verifyChain(certChain, tlsaRecords, opts?)`",
2310
+ "body": "(NEW) — walks the peer cert chain (leaf-first DER buffers — typically `sock.getPeerCertificate(true).raw`) and confirms at least one TLSA record matches per the record's usage / selector / mtype. SMTP outbound (RFC 7672) only honors `DANE-TA` (2) and `DANE-EE` (3); `PKIX-TA` (0) and `PKIX-EE` (1) require a full PKIX path validator + CA-bundle and are refused unless the operator opts in via `allowPkixModes`. Selector `Cert` (0) compares the full DER; selector `SPKI` (1) compares the SubjectPublicKeyInfo bytes (extracted via the framework ASN.1 walker). Matching types: `Full`, sha-two-family at the short-digest length, sha-two-family at the long-digest length. Returns `{ ok, matches: [{ tlsaIndex, certIndex, usage, mtype }], errors }`."
2311
+ },
2312
+ {
2313
+ "title": "ASN.1 walker",
2314
+ "body": "`lib/asn1-der.js` `readNode` now surfaces a `.raw` field on each node (header + value bytes from the source buffer) so SPKI extraction can return the exact wire bytes for byte-identical TLSA matching."
2315
+ },
2316
+ {
2317
+ "title": "Smoke-runner LPT scheduling",
2318
+ "body": "`test/smoke.js` parallel mode now uses Longest-Processing-Time-first scheduling with a continuous worker queue. Per-test durations persist under `.test-output/smoke-timings.json` keyed by `process.platform` (so host win32/darwin and Linux container don't pollute each other's medians) with a 5-run history per test. Replaces the prior batched `Promise.all(slice(i, i + PARALLEL))` scheduling that left workers idle when one batch contained the long-tail test."
2319
+ },
2320
+ {
2321
+ "title": "Wiki failure-detail bugfix",
2322
+ "body": "`examples/wiki/test/e2e.js` was printing the per-example failure list AFTER the assert that throws on failure → operators only saw \"1 of 178 failed\" without WHICH example. Reordered so failure detail prints first."
2323
+ }
2324
+ ]
2325
+ }
2326
+ ],
2327
+ "references": [
2328
+ {
2329
+ "label": "RFC 6698",
2330
+ "url": "https://www.rfc-editor.org/rfc/rfc6698.html"
2331
+ },
2332
+ {
2333
+ "label": "RFC 7672",
2334
+ "url": "https://www.rfc-editor.org/rfc/rfc7672.html"
2335
+ }
2336
+ ]
2337
+ },
2338
+ {
2339
+ "version": "0.7.37",
2340
+ "date": "2026-05-05",
2341
+ "headline": "RFC 8460 TLS-RPT submission transport via `b.network.smtp.tlsRpt`",
2342
+ "summary": "Adds `fetchPolicy` + `submit`. The previous `recordShape` only generated the JSON; operators had to wire submission themselves.",
2343
+ "sections": [
2344
+ {
2345
+ "heading": "Added",
2346
+ "items": [
2347
+ {
2348
+ "title": "`b.network.smtp.tlsRpt.fetchPolicy(domain, opts)`",
2349
+ "body": "(NEW) — reads the RFC 8460 §3 `_smtp._tls.<domain>` TXT record and returns `{ version, rua: [string,...] }` where `rua` is the comma-separated list of report URIs (`https://` and `mailto:`) the recipient publishes. Returns `null` when no record is published. `opts.dnsLookup` is operator-supplied so the call composes with `b.network.dns` (DoH / DoT) without a hard dep; falls back to `node:dns/promises.resolveTxt`."
2350
+ },
2351
+ {
2352
+ "title": "`b.network.smtp.tlsRpt.submit(report, opts)`",
2353
+ "body": "(NEW) — submits a TLS-RPT report to the published rua endpoints. `https://` URIs receive an HTTPS `POST` with `Content-Type: application/tlsrpt+gzip` and a gzip-compressed JSON body per RFC 8460 §6.2. `mailto:` URIs return a prepared `{ to, subject, contentType, encoding, body }` object so operators hand it to `b.mail` with their configured relay (the framework doesn't bake an SMTP relay in — operators wire transport per their environment). Per-endpoint result records carry `{ uri, kind, ok, status, error }` so a failure on one rua doesn't cascade. Routes through `b.httpClient` so SSRF + DNS-pin + retry policy come for free."
2354
+ }
2355
+ ]
2356
+ }
2357
+ ],
2358
+ "references": [
2359
+ {
2360
+ "label": "RFC 8460",
2361
+ "url": "https://www.rfc-editor.org/rfc/rfc8460.html"
2362
+ }
2363
+ ]
2364
+ },
2365
+ {
2366
+ "version": "0.7.36",
2367
+ "date": "2026-05-05",
2368
+ "headline": "RFC 7633 must-staple enforcement via `b.network.tls.ocsp.inspectMustStaple`",
2369
+ "summary": "Adds `inspectMustStaple` + a `requireMustStaple` predicate. The TLS Feature extension (OID `1.3.6.1.5.5.7.1.24`) is the cert-side contract that says \"every connection MUST carry an OCSP staple\"; clients that ignore the extension defeat its purpose.",
2370
+ "sections": [
2371
+ {
2372
+ "heading": "Added",
2373
+ "items": [
2374
+ {
2375
+ "title": "`b.network.tls.ocsp.inspectMustStaple(rawDer)`",
2376
+ "body": "(NEW) — walks the X.509 cert DER and reads the TLS Feature extension. Returns `{ mustStaple, features }` where `mustStaple === true` when status_request (5) is in the feature list. Tolerant of malformed cert input (returns `{ mustStaple: false, features: [] }` rather than throwing)."
2377
+ },
2378
+ {
2379
+ "title": "`b.network.tls.ocsp.requireMustStaple(opts)`",
2380
+ "body": "(NEW) — operator predicate `(peerCert, ctx) → Error|null`. Refuses connections where the cert advertises must-staple but `ctx.ocspBytes` is empty/missing per RFC 7633 §4.2.3. `opts.enforceUnconditional: true` extends the policy to refuse staple-less responses on certs that don't carry must-staple — operator-stricter-than-RFC posture. Error codes: `tls/ocsp-no-cert` / `tls/ocsp-must-staple-violated` / `tls/ocsp-staple-required`."
2381
+ }
2382
+ ]
2383
+ }
2384
+ ],
2385
+ "references": [
2386
+ {
2387
+ "label": "RFC 7633",
2388
+ "url": "https://www.rfc-editor.org/rfc/rfc7633.html"
2389
+ }
2390
+ ]
2391
+ },
2392
+ {
2393
+ "version": "0.7.35",
2394
+ "date": "2026-05-05",
2395
+ "headline": "DKIM verify hardening: process-local key cache + RSA modulus-size enforcement + `l=` warning",
2396
+ "summary": "Process-local DKIM key cache — `_fetchDkimKey` now caches each successful TXT lookup keyed by `<selector>._domainkey.<domain>` for 5 minutes (TTL-bounded; rotated keys propagate within minutes). LRU-ish eviction at 1024 entries. Mailing-list fan-out and bulk-replay scenarios that previously hammered DNS for the same selector now hit the cache.",
2397
+ "sections": [
2398
+ {
2399
+ "heading": "Fixed",
2400
+ "items": [
2401
+ {
2402
+ "title": "Process-local DKIM key cache",
2403
+ "body": "`_fetchDkimKey` now caches each successful TXT lookup keyed by `<selector>._domainkey.<domain>` for 5 minutes (TTL-bounded; rotated keys propagate within minutes). LRU-ish eviction at 1024 entries. Mailing-list fan-out and bulk-replay scenarios that previously hammered DNS for the same selector now hit the cache."
2404
+ },
2405
+ {
2406
+ "title": "RSA modulus-size enforcement",
2407
+ "body": "RSA keys < 1024 bits hard-fail per RFC 8301 §3.1; keys < 2048 bits emit a `rsa-key-weak` warning so operators can quarantine while transitioning. Reads `keyObj.asymmetricKeyDetails.modulusLength` from `node:crypto`."
2408
+ },
2409
+ {
2410
+ "title": "`l=` body-length warning on verify",
2411
+ "body": "the framework refuses `l=` at SIGN-time per v0.7.18 (M³AAWG / Gmail / Microsoft 365 guidance). On VERIFY of inbound mail, an `l=` tag now surfaces as `l-tag-present: append-after-signature exposure (RFC 6376 §8.2)` in the result's `warnings` array. The verifier still honors the cap so legitimate senders that use `l=` don't break, but operators see the exposure. Verify-result shape gains a `warnings` field on `pass` / `fail` results (alongside the existing `errors` array)."
2412
+ }
2413
+ ]
2414
+ }
2415
+ ],
2416
+ "references": [
2417
+ {
2418
+ "label": "RFC 8301",
2419
+ "url": "https://www.rfc-editor.org/rfc/rfc8301.html"
2420
+ },
2421
+ {
2422
+ "label": "RFC 6376",
2423
+ "url": "https://www.rfc-editor.org/rfc/rfc6376.html"
2424
+ }
2425
+ ]
2426
+ },
2427
+ {
2428
+ "version": "0.7.34",
2429
+ "date": "2026-05-05",
2430
+ "headline": "`b.network.tls.ct.verifyScts` with real RFC 6962 / 9162 Signed Certificate Timestamp signature",
2431
+ "summary": "verification, plus `b.network.tls.ct.parseScts` ASN.1 walker.",
2432
+ "sections": [
2433
+ {
2434
+ "heading": "Added",
2435
+ "items": [
2436
+ {
2437
+ "title": "`b.network.tls.ct.parseScts(rawDer)`",
2438
+ "body": "(NEW) — full ASN.1 walk; returns `[{ version, logIdHex, timestamp, signature, ... }]` or `[]` when no SCT extension present (tolerant of malformed cert input — returns empty rather than throwing)."
2439
+ },
2440
+ {
2441
+ "title": "`b.network.tls.ct.verifyScts(rawDer, opts)`",
2442
+ "body": "(NEW) — full verification. `opts.logKeys` maps log_id (hex SHA-256 of the log's pubkey) → PEM public key; operators populate from the Chrome CT log list (the framework doesn't bake log keys in — they rotate). Returns `{ ok, verifiedCount, totalScts, reason, scts: [{ logIdHex, verified, ... }] }`."
2443
+ },
2444
+ {
2445
+ "title": "`b.network.tls.ct.requireScts({ minScts, logKeys })`",
2446
+ "body": "(UPGRADED) — was OID-presence-only; now performs full signature verification. Reason-prefixed error codes: `tls/ct-no-cert` / `tls/ct-no-sct-extension` / `tls/ct-insufficient-verified` / `tls/ct-not-verified`. Breaking — `network.tls.ct.APPROVED_LOGS` removed. The hardcoded log-list instance was always a stand-in; CT logs rotate keys and add/remove entries on the order of months. Operators now pass `logKeys` per call. Pre-v1, no compat shim."
2447
+ }
2448
+ ]
2449
+ }
2450
+ ],
2451
+ "references": [
2452
+ {
2453
+ "label": "RFC 6962",
2454
+ "url": "https://www.rfc-editor.org/rfc/rfc6962.html"
2455
+ }
2456
+ ]
2457
+ },
2458
+ {
2459
+ "version": "0.7.33",
2460
+ "date": "2026-05-05",
2461
+ "headline": "`b.network.tls.ocsp.requireGood` with real signature verification, plus a focused ASN.1 DER walker",
2462
+ "summary": "for the framework's narrow cryptographic uses. `lib/asn1-der.js` (NEW) — minimal DER walker exposing `readNode` / `readSequence` / `readOid` / `readOctetString` / `readUnsignedInt` / `readBitString` / `unwrapExplicit`. Refuses BER indefinite-length encoding (DER-only).",
2463
+ "sections": [
2464
+ {
2465
+ "heading": "Added",
2466
+ "items": [
2467
+ {
2468
+ "title": "`lib/asn1-der.js`",
2469
+ "body": "(NEW) — minimal DER walker exposing `readNode` / `readSequence` / `readOid` / `readOctetString` / `readUnsignedInt` / `readBitString` / `unwrapExplicit`. Refuses BER indefinite-length encoding (DER-only). Throws `Asn1Error` with stable error codes (`asn1/short` / `asn1/bad-length` / `asn1/oid-malformed` / `asn1/wrong-tag` / etc.)."
2470
+ },
2471
+ {
2472
+ "title": "`b.network.tls.ocsp.parseResponse(der)`",
2473
+ "body": "RFC 6960 OCSPResponse parser. Returns `{ status, basic: { tbsResponseDataDer, signatureAlgorithmOid, signature, responses[] } }`. Each response carries `{ certIdSerialHex, certStatus, thisUpdate, nextUpdate }`."
2474
+ },
2475
+ {
2476
+ "title": "`b.network.tls.ocsp.evaluate(der, { issuerPem, serialHex? })`",
2477
+ "body": "full verification: parse + signature-verify against the operator-supplied issuer cert (rsa-sha256/384/512 + ecdsa-sha256/384/512) + certStatus check. Returns `{ ok, status, certStatus, thisUpdate, nextUpdate, signatureValid, errors }`."
2478
+ },
2479
+ {
2480
+ "title": "`b.network.tls.ocsp.requireGood(opts)`",
2481
+ "body": "(NEW) — connect + parse + signature-verify + certStatus check in one call. Throws `tls/ocsp-not-good` when the OCSP response is malformed, unsigned, signed-by-wrong-key, or carries `certStatus=revoked/unknown`. The honest counterpart to v0.7.31's `requireStapled` rename: `requireStapled` checks presence only (existing wrapper), `requireGood` does the full RFC 6960 validation."
2482
+ }
2483
+ ]
2484
+ }
2485
+ ],
2486
+ "references": [
2487
+ {
2488
+ "label": "RFC 6960",
2489
+ "url": "https://www.rfc-editor.org/rfc/rfc6960.html"
2490
+ }
2491
+ ]
2492
+ },
2493
+ {
2494
+ "version": "0.7.32",
2495
+ "date": "2026-05-05",
2496
+ "headline": "Wiki backfill for v0",
2497
+ "summary": "7.18-31 primitives + parallel e2e example execution.",
2498
+ "sections": [
2499
+ {
2500
+ "heading": "Added",
2501
+ "items": [
2502
+ {
2503
+ "title": "Wiki primitive-signature regex relaxed",
2504
+ "body": "was `b.X.Y(...)` two-level only; now also matches `b.X(args)` top-level functions like `b.pick`, `b.lazyRequire`, `b.createApp`."
2505
+ },
2506
+ {
2507
+ "title": "Parallel e2e example execution",
2508
+ "body": "`examples/wiki/test/validate-primitive-sections.js` `runExamples()` now respects `SMOKE_PARALLEL=N` (capped at 64) and forks the per-example child processes in parallel batches. Timing: 78s sequential → 15s parallel-64 (5.2× speedup). Same env var as smoke; `SMOKE_PARALLEL=1` falls back to sequential for diagnosis."
2509
+ }
2510
+ ]
2511
+ }
2512
+ ]
2513
+ },
2514
+ {
2515
+ "version": "0.7.31",
2516
+ "date": "2026-05-05",
2517
+ "headline": "DKIM verify + ARC per-hop signature verification + wiki missing-section gate",
2518
+ "summary": "`b.mail.dkim.verify(rfc822, opts)` (NEW) — RFC 6376 verifier counterpart to the existing signer.",
2519
+ "sections": [
2520
+ {
2521
+ "heading": "Fixed",
2522
+ "items": [
2523
+ {
2524
+ "title": "`b.mail.dkim.verify(rfc822, opts)`",
2525
+ "body": "(NEW) — RFC 6376 verifier counterpart to the existing signer. Walks every `DKIM-Signature` header in the message, parses the tag list, fetches the signing public key from DNS TXT at `<selector>._domainkey.<domain>` (operator passes `dnsLookup` callback or falls back to `node:dns/promises.resolveTxt`), canonicalizes the body + headers per the `c=` tag, and runs `node:crypto.verify`. Returns one result per signature: `{ d, s, alg, result, errors }` where result is `pass` / `fail` / `permerror` / `temperror` / `none`. Supports `rsa-sha256` and `ed25519-sha256`."
2526
+ },
2527
+ {
2528
+ "title": "`b.mail.arc.verify(rfc822, opts)`",
2529
+ "body": "(UPGRADED) — was structural-only; now performs full per-hop ARC-Message-Signature + ARC-Seal signature verification per RFC 8617 §5.1.1 and §5.1.2. AMS reuses the DKIM verifier (identical cryptographic shape); AS canonicalizes the chain of prior AAR/AMS/AS headers + own AAR/AMS plus AS-with-empty-`b=` per §5.1.2. Returns `{ chainStatus, hopCount, cv, hops: [{ instance, amsResult, asResult, ... }] }`. Chain validity per §5.2 — most recent AS's `cv=` must reflect upstream validity."
2530
+ },
2531
+ {
2532
+ "title": "DKIM signer fold-output fix",
2533
+ "body": "`_foldSignatureHeader` was producing malformed wire (`v=1\\r\\n\\ta=rsa-sha256;\\r\\n\\t...` — missing `;` after the first segment). RFC 6376 §3.2 requires `;` to stay on the prior line at fold boundaries. The corrected output is `v=1;\\r\\n\\ta=rsa-sha256;\\r\\n\\t...`. Pre-v1, no compat shims — operators rotate keys / rebuild any saved signatures."
2534
+ },
2535
+ {
2536
+ "title": "`b.network.tls.ocsp.requireGood` → `requireStapled`",
2537
+ "body": "(RENAMED) — the wrapper claimed \"good\" but only checked stapling-presence + non-empty bytes; full OCSP-response signature verification is a follow-up patch alongside an ASN.1 DER helper. The honest name keeps the surface from claiming verification it doesn't perform."
2538
+ },
2539
+ {
2540
+ "title": "Wiki missing-section gate",
2541
+ "body": "`examples/wiki/test/validate-primitive-sections.js` now enumerates every operator-facing primitive on `b.*` and refuses release if a primitive lacks a documented wiki section. Pre-v0.7.31 backlog explicitly listed in `UNDOCUMENTED_BACKLOG` with per-primitive reasons (most are documented under existing pages with prose-form rather than signature-form headings — backfilling the headings is a separate sweep). New primitives shipped from v0.7.31 forward MUST either land with a wiki section OR add an explicit BACKLOG entry."
2542
+ }
2543
+ ]
2544
+ }
2545
+ ],
2546
+ "references": [
2547
+ {
2548
+ "label": "RFC 822",
2549
+ "url": "https://www.rfc-editor.org/rfc/rfc822.html"
2550
+ },
2551
+ {
2552
+ "label": "RFC 6376",
2553
+ "url": "https://www.rfc-editor.org/rfc/rfc6376.html"
2554
+ },
2555
+ {
2556
+ "label": "RFC 8617",
2557
+ "url": "https://www.rfc-editor.org/rfc/rfc8617.html"
2558
+ }
2559
+ ]
2560
+ },
2561
+ {
2562
+ "version": "0.7.30",
2563
+ "date": "2026-05-05",
2564
+ "headline": "`b.mail.spf` + `b.mail.dmarc` + `b.mail.arc` inbound mail authentication-results verification",
2565
+ "summary": "family. Counterpart to the existing outbound DKIM signer. Operators receiving mail (incoming webhooks, customer-support inboxes, mailing-list ingestion, .eml uploads) evaluate sender authenticity and decide on accept / quarantine / reject.",
2566
+ "sections": [
2567
+ {
2568
+ "heading": "Added",
2569
+ "items": [
2570
+ {
2571
+ "title": "`b.mail.spf.verify({ ip, mailFrom, helo, dnsLookup })`",
2572
+ "body": "RFC 7208 SPF check with ip4 / ip6 / include / all mechanisms; recursive include resolution with the 10-DNS-lookup ceiling per §4.6.4; returns `{ result, domain, explanation, lookupCount }` where result is one of `pass` / `fail` / `softfail` / `neutral` / `none` / `temperror` / `permerror`."
2573
+ },
2574
+ {
2575
+ "title": "`b.mail.dmarc.evaluate({ from, spf, dkim, dnsLookup })`",
2576
+ "body": "RFC 7489 DMARC alignment + policy resolution; fetches `_dmarc.<domain>` TXT record, evaluates `aspf` / `adkim` (relaxed/strict) alignment between From-header domain and SPF/DKIM authenticated domains, returns `{ result, policy, alignment, recommendedAction }` where recommendedAction is `deliver` / `quarantine` / `reject` per the published policy."
2577
+ },
2578
+ {
2579
+ "title": "`b.mail.arc.verify(rfc822)`",
2580
+ "body": "RFC 8617 ARC chain inspection; parses `ARC-Seal` / `ARC-Message-Signature` / `ARC-Authentication-Results` headers, returns `{ chainStatus, hopCount, hops }` with status `pass` / `fail` / `none` (structural integrity only — per-hop signature verification deferred to a follow-up patch)."
2581
+ },
2582
+ {
2583
+ "title": "Wiki e2e parallelizable",
2584
+ "body": "added `BLAMEJS_E2E_DATA_DIR` env override so the host wiki e2e and the Linux container wiki e2e can run in parallel without colliding on `examples/wiki/data-e2e`. Out of scope (deferred): SPF a / mx / exists / ptr / redirect mechanisms (operator-supplied dnsLookup callback handles the rare cases via permerror); full DKIM verify path (composes the canonicalization helpers from lib/mail-dkim.js but needs the symmetric verifier — substantial follow-up); ARC per-hop signature verification (depends on the DKIM verifier). The framework gives operators the OUTCOME-policy layer + the structural verifier; the deferred items address the residual signature-verification surface."
2585
+ }
2586
+ ]
2587
+ }
2588
+ ],
2589
+ "references": [
2590
+ {
2591
+ "label": "RFC 7208",
2592
+ "url": "https://www.rfc-editor.org/rfc/rfc7208.html"
2593
+ },
2594
+ {
2595
+ "label": "RFC 7489",
2596
+ "url": "https://www.rfc-editor.org/rfc/rfc7489.html"
2597
+ },
2598
+ {
2599
+ "label": "RFC 822",
2600
+ "url": "https://www.rfc-editor.org/rfc/rfc822.html"
2601
+ },
2602
+ {
2603
+ "label": "RFC 8617",
2604
+ "url": "https://www.rfc-editor.org/rfc/rfc8617.html"
2605
+ }
2606
+ ]
2607
+ },
2608
+ {
2609
+ "version": "0.7.29",
2610
+ "date": "2026-05-05",
2611
+ "headline": "`b.network.smtp` MTA-STS + DANE + TLS-RPT outbound SMTP gates. Gmail and Microsoft 365 penalize",
2612
+ "summary": "senders without these policies; the framework now ships the operator surface for verifying recipient-domain policies before opening the SMTP socket. `b.network.smtp.mtaSts.fetch(domain)` — fetches `https://mta-sts.<domain>/.well-known/mta-sts.txt`, parses the policy text, returns `{ version, mode, mx[], max_age }` or `null` (domain doesn't publish).",
2613
+ "sections": [
2614
+ {
2615
+ "heading": "Added",
2616
+ "items": [
2617
+ {
2618
+ "title": "`b.network.smtp.mtaSts.fetch(domain)`",
2619
+ "body": "fetches `https://mta-sts.<domain>/.well-known/mta-sts.txt`, parses the policy text, returns `{ version, mode, mx[], max_age }` or `null` (domain doesn't publish). TTL-bounded `b.cache` per-process."
2620
+ },
2621
+ {
2622
+ "title": "`b.network.smtp.mtaSts.matchMx(mxHost, mxList)`",
2623
+ "body": "RFC 8461 §3.2 wildcard-aware MX matcher (`*.example.com` matches `mx.example.com` but not `a.b.example.com`)."
2624
+ },
2625
+ {
2626
+ "title": "`b.network.smtp.dane.tlsa(domain, port?)`",
2627
+ "body": "DNS TYPE 52 lookup via `node:dns.resolveTlsa`; returns `[{ usage, selector, mtype, dataHex }, ...]` for the `_<port>._tcp.<domain>` qname."
2628
+ },
2629
+ {
2630
+ "title": "`b.network.smtp.dane.recordShape(rec)`",
2631
+ "body": "adds RFC 6698 human-readable labels (`PKIX-TA` / `PKIX-EE` / `DANE-TA` / `DANE-EE`; `Cert` / `SPKI`; `Full` / `SHA-256` / `SHA-512`)."
2632
+ },
2633
+ {
2634
+ "title": "`b.network.smtp.tlsRpt.recordShape(opts)`",
2635
+ "body": "RFC 8460 TLS-RPT JSON report-shape generator with `organization-name` / `date-range` / `policies[].summary.total-{successful,failure}-session-count` / `failure-details`. Operators wire their report transport (SMTP / HTTPS) on top. Out of scope (deferred): full DANE certificate-chain verification per RFC 6698 (needs ASN.1 cert parsing); TLS-RPT submission transport (operator-side); DNSSEC ad-bit validation (operators pin to a DNSSEC-validating resolver externally)."
2636
+ }
2637
+ ]
2638
+ }
2639
+ ],
2640
+ "references": [
2641
+ {
2642
+ "label": "RFC 8461",
2643
+ "url": "https://www.rfc-editor.org/rfc/rfc8461.html"
2644
+ },
2645
+ {
2646
+ "label": "RFC 6698",
2647
+ "url": "https://www.rfc-editor.org/rfc/rfc6698.html"
2648
+ },
2649
+ {
2650
+ "label": "RFC 8460",
2651
+ "url": "https://www.rfc-editor.org/rfc/rfc8460.html"
2652
+ }
2653
+ ]
2654
+ },
2655
+ {
2656
+ "version": "0.7.28",
2657
+ "date": "2026-05-05",
2658
+ "headline": "`ML_KEM_768_X25519` second envelope (TLS-interop hybrid)",
2659
+ "summary": "The IETF / Cloudflare / Chrome standardized hybrid for TLS 1.3 (codepoint 0x11EC, draft-kwiatkowski-tls-ecdhe-mlkem). Smaller payload than ML-KEM-1024 + P-384 (~1.1 KB vs ~1.6 KB), wider interop with non-blamejs peers using the same hybrid (Cloudflare Workers / Chrome / blamejs-on-the-other-side).",
2660
+ "sections": [
2661
+ {
2662
+ "heading": "Added",
2663
+ "items": [
2664
+ {
2665
+ "title": "`b.crypto.SUPPORTED_KEM_ALGORITHMS`",
2666
+ "body": "lists every KEM hybrid the framework accepts on decrypt — `ml-kem-1024` / `ml-kem-1024-p384` (default) / `ml-kem-768-x25519` — for compliance audit visibility. `ACTIVE.KEM` stays on `ML_KEM_1024_P384`; the new hybrid is opt-in for cross-system interop."
2667
+ }
2668
+ ]
2669
+ }
2670
+ ]
2671
+ },
2672
+ {
2673
+ "version": "0.7.27",
2674
+ "date": "2026-05-05",
2675
+ "headline": "`b.compliance` top-level posture coordinator. Single source of truth for \"what regulatory posture",
2676
+ "summary": "is this deployment running under?\". Primitives with a `compliancePosture` opt fall back to the global setting when the operator hasn't passed one explicitly. Surface: `b.compliance.set(\"hipaa\")` / `b.compliance.current()` / `b.compliance.assert(\"hipaa\")` / `b.compliance.clear()`.",
2677
+ "sections": [
2678
+ {
2679
+ "heading": "Added",
2680
+ "items": [
2681
+ {
2682
+ "title": "`KNOWN_POSTURES`",
2683
+ "body": "`hipaa` / `pci-dss` / `gdpr` / `soc2` / `dora` / `sox`."
2684
+ },
2685
+ {
2686
+ "title": "Boot-time only",
2687
+ "body": "`set()` to a different posture after one is already active throws `compliance/already-set` (runtime switches forbidden — half-set state across initialized primitives is worse than no state); same-value re-set is idempotent."
2688
+ },
2689
+ {
2690
+ "title": "Audit emissions",
2691
+ "body": "`compliance.posture.set` on every successful set, `compliance.posture.cleared` on `clear()`. Wired into `b.gateContract.resolveProfileAndPosture` so every guard-* family member picks up the global posture as fallback when no per-call `compliancePosture` is given. Operators with multi-tenant deploys keep using per-call `compliancePosture` opts to override the global."
2692
+ }
2693
+ ]
2694
+ }
2695
+ ]
2696
+ },
2697
+ {
2698
+ "version": "0.7.26",
2699
+ "date": "2026-05-05",
2700
+ "headline": "`b.network.tls.ocsp` + `b.network.tls.ct` operator surface for OCSP enforcement and CT SCT",
2701
+ "summary": "inspection. `b.network.tls.ocsp.connect(opts)` / `.requireGood(opts)` — wraps `tls.connect({ requestOCSP: true })` to give operators a one-call shape for \"connect with OCSP request\" and \"refuse if peer didn't staple a good OCSP response\".",
2702
+ "sections": [
2703
+ {
2704
+ "heading": "Added",
2705
+ "items": [
2706
+ {
2707
+ "title": "`b.network.tls.ocsp.connect(opts)` / `.requireGood(opts)`",
2708
+ "body": "wraps `tls.connect({ requestOCSP: true })` to give operators a one-call shape for \"connect with OCSP request\" and \"refuse if peer didn't staple a good OCSP response\". Returns `{ authorized, ocspBytes, peerCert }` on success; rejects with `tls/ocsp-not-stapled` or `tls/ocsp-empty` from `requireGood` when the peer didn't deliver."
2709
+ },
2710
+ {
2711
+ "title": "`b.network.tls.ct`",
2712
+ "body": "Certificate Transparency (RFC 6962 / 9162) operator surface. `ct.inspect(rawDer)` returns `{ hasSctExtension, rawLength }` after byte-pattern locating the SCT extension OID `1.3.6.1.4.1.11129.2.4.2`. `ct.requireScts({ minScts? })` returns a predicate operators wire into their TLS-connect outcome flow — refuses peer certs lacking the SCT extension with `tls/ct-no-sct-extension`. `ct.APPROVED_LOGS` lists the 2026-current Google Argon / Cloudflare Nimbus / DigiCert Yeti / Sectigo Sabre / LetsEncrypt Oak shards. Out of scope this patch (deferred): full ASN.1 OCSPRequest building and OCSPResponse parsing; full SCT-signature verification against log pubkeys (presence-only enforcement until the ASN.1 dependency lands). The framework gives operators the OUTCOME-policy layer; node:tls already does the protocol."
2713
+ }
2714
+ ]
2715
+ }
2716
+ ],
2717
+ "references": [
2718
+ {
2719
+ "label": "RFC 6962",
2720
+ "url": "https://www.rfc-editor.org/rfc/rfc6962.html"
2721
+ }
2722
+ ]
2723
+ },
2724
+ {
2725
+ "version": "0.7.25",
2726
+ "date": "2026-05-05",
2727
+ "headline": "`b.dora` DORA Article 17 incident-reporting workflow",
2728
+ "summary": "Financial entities subject to DORA (Regulation (EU) 2022/2554) classify, document, and report ICT-related incidents per the Article 17 RTS template (Commission Delegated Regulation 2024/1772). The new primitive produces the harmonized record their submission code drops into the regulator's API.",
2729
+ "sections": [
2730
+ {
2731
+ "heading": "Added",
2732
+ "items": [
2733
+ {
2734
+ "title": "`b.dora.create({ audit })`",
2735
+ "body": "Returns `{ classify, report, draftFinalReport }`. `classify(input)` evaluates the impact dimensions (`severityIndicator` / `affectedClients` / `economicImpact.eur` / `geographicScope` / `durationMs` / `dataAffected` / `reputationalImpact`) against the RTS Article 1 thresholds and returns `{ classification: \"major\"|\"significant\"|\"minor\", mustReport, mustReportInitialByMs, reasons }`. `report(input)` validates and builds the three-stage RTS-shaped record (initial / intermediate / final) with Article 19 deadlines auto-computed (initial → +72h intermediate due; intermediate → +30 days final due). `draftFinalReport(record)` returns a final-stage draft with the Article 19(6) fields ready for operator fill-in (`rootCause`, `remediationActions`, `lessonsLearned`, `preventiveMeasures`)."
2736
+ },
2737
+ {
2738
+ "title": "RTS-aligned threshold constants",
2739
+ "body": "`b.dora.MAJOR_INCIDENT_THRESHOLDS` and `b.dora.SIGNIFICANT_INCIDENT_THRESHOLDS` expose the numeric thresholds: 100k clients / 100k EUR / 2+ member states / 8h critical-process disruption (major); 10k clients / 10k EUR / 2h disruption (significant)."
2740
+ },
2741
+ {
2742
+ "title": "Audit emissions",
2743
+ "body": "`dora.incident.classified` / `dora.incident.reported` / `dora.incident.draftFinal` so the operator's audit chain captures every classification + submission moment alongside the rest of the compliance-relevant surface."
2744
+ }
2745
+ ]
2746
+ },
2747
+ {
2748
+ "heading": "Changed",
2749
+ "items": [
2750
+ {
2751
+ "title": "Operator-side submission stays out of scope",
2752
+ "body": "Submission channel + credentials to ESAs / national supervisors are operator-specific; the primitive emits the RTS-shaped record and the operator's existing transport drops it into the regulator's API. The framework owns the record shape, not the wire."
2753
+ }
2754
+ ]
2755
+ }
2756
+ ],
2757
+ "references": [
2758
+ {
2759
+ "label": "DORA Regulation (EU) 2022/2554",
2760
+ "url": "https://eur-lex.europa.eu/eli/reg/2022/2554/oj"
2761
+ },
2762
+ {
2763
+ "label": "Commission Delegated Regulation (EU) 2024/1772",
2764
+ "url": "https://eur-lex.europa.eu/eli/reg_del/2024/1772/oj"
2765
+ }
2766
+ ]
2767
+ },
2768
+ {
2769
+ "version": "0.7.24",
2770
+ "date": "2026-05-05",
2771
+ "headline": "`b.retention.complianceFloor` accessor for regulatory minimum-retention windows",
2772
+ "summary": "Operators get an explicit floor for record-retention TTLs keyed by compliance posture, so a too-short candidate TTL is silently bumped to the regulatory minimum instead of being honored as-is.",
2773
+ "sections": [
2774
+ {
2775
+ "heading": "Added",
2776
+ "items": [
2777
+ {
2778
+ "title": "`b.retention.complianceFloor(posture, candidateTtlMs?)`",
2779
+ "body": "Returns the effective TTL that meets or exceeds the regulatory floor for the named posture. When `candidateTtlMs` exceeds the floor it wins; when it's below, the floor takes over. Throws `retention/unknown-posture` on unknown names so a typo at boot doesn't silently produce a non-compliant retention window."
2780
+ },
2781
+ {
2782
+ "title": "`b.retention.COMPLIANCE_RETENTION_FLOOR_MS`",
2783
+ "body": "Exposes the per-posture regulatory minimums for compliance-audit visibility: `pci-dss` 365 days (PCI-DSS Req 10.7.1), `hipaa` 6 years (45 CFR §164.316(b)(2)), `sox` 7 years (Sarbanes-Oxley §802), `soc2` 1 year, `dora` 5 years (DORA Article 17)."
2784
+ }
2785
+ ]
2786
+ }
2787
+ ],
2788
+ "references": [
2789
+ {
2790
+ "label": "PCI-DSS v4.0 Requirement 10.7",
2791
+ "url": "https://www.pcisecuritystandards.org/document_library/"
2792
+ },
2793
+ {
2794
+ "label": "45 CFR §164.316 HIPAA Security Rule",
2795
+ "url": "https://www.ecfr.gov/current/title-45/subtitle-A/subchapter-C/part-164"
2796
+ },
2797
+ {
2798
+ "label": "Sarbanes-Oxley Act §802",
2799
+ "url": "https://www.govinfo.gov/content/pkg/PLAW-107publ204/html/PLAW-107publ204.htm"
2800
+ },
2801
+ {
2802
+ "label": "DORA Regulation (EU) 2022/2554",
2803
+ "url": "https://eur-lex.europa.eu/eli/reg/2022/2554/oj"
2804
+ }
2805
+ ]
2806
+ },
2807
+ {
2808
+ "version": "0.7.23",
2809
+ "date": "2026-05-05",
2810
+ "headline": "DNS-over-HTTPS default-on",
2811
+ "summary": "Default-on DoH for outbound DNS — `lib/network-dns.js` now uses Cloudflare DoH (`https://cloudflare-dns.com/dns-query`) for hostname resolution by default when neither `useDnsOverHttps()` nor `useDnsOverTls()` has been called explicitly.",
2812
+ "sections": [
2813
+ {
2814
+ "heading": "Added",
2815
+ "items": [
2816
+ {
2817
+ "title": "Default-on DoH for outbound DNS",
2818
+ "body": "`lib/network-dns.js` now uses Cloudflare DoH (`https://cloudflare-dns.com/dns-query`) for hostname resolution by default when neither `useDnsOverHttps()` nor `useDnsOverTls()` has been called explicitly. Privacy-respecting (encrypted DNS over TLS to a non-ISP resolver), aligned with Core Rule §3 (\"security defaults are not opt-in\")."
2819
+ },
2820
+ {
2821
+ "title": "Local-form hosts route through node:dns",
2822
+ "body": "RFC 6761 / 6762 special-form names (`localhost`, `*.localhost`, `*.local`, `*.test`, `*.invalid`, `*.internal`, `*.intranet`, `*.lan`, `*.home`, `*.corp`) AND IP literals skip DoH and use node:dns directly so `/etc/hosts` lookups + dev workflows continue to resolve correctly."
2823
+ },
2824
+ {
2825
+ "title": "Operator opt-out via `b.network.dns.useSystemResolver()`",
2826
+ "body": "split-horizon / internal-DNS deployments call this once at boot and every lookup routes through the OS resolver thereafter."
2827
+ },
2828
+ {
2829
+ "title": "Env override `BLAMEJS_DNS_TRANSPORT`",
2830
+ "body": "operators set `system` (force OS resolver), `dot` (force Cloudflare DNS-over-TLS at 1.1.1.1:853), or leave unset (default DoH)."
2831
+ }
2832
+ ]
2833
+ }
2834
+ ],
2835
+ "references": [
2836
+ {
2837
+ "label": "RFC 6761",
2838
+ "url": "https://www.rfc-editor.org/rfc/rfc6761.html"
2839
+ }
2840
+ ]
2841
+ },
2842
+ {
2843
+ "version": "0.7.22",
2844
+ "date": "2026-05-05",
2845
+ "headline": "DKIM dual-signer + soc2-cc7 → soc2 posture rename",
2846
+ "summary": "`b.mail.dkim.dualSigner` — RFC 8463 §3 transition signer that produces messages with BOTH a legacy RSA-SHA-256 DKIM-Signature AND an Ed25519-SHA-256 DKIM-Signature header. Receivers without Ed25519 support validate the RSA signature; receivers that prefer Ed25519 validate the post-quantum-friendlier signature.",
2847
+ "sections": [
2848
+ {
2849
+ "heading": "Added",
2850
+ "items": [
2851
+ {
2852
+ "title": "`b.mail.dkim.dualSigner`",
2853
+ "body": "RFC 8463 §3 transition signer that produces messages with BOTH a legacy RSA-SHA-256 DKIM-Signature AND an Ed25519-SHA-256 DKIM-Signature header. Receivers without Ed25519 support validate the RSA signature; receivers that prefer Ed25519 validate the post-quantum-friendlier signature. Operators rolling off RSA-SHA-256 wire `b.mail.dkim.dualSigner({ domain, rsa: { selector, privateKey }, eddsa: { selector, privateKey } })` and pass the result anywhere a regular DKIM signer is accepted; `sign()` produces a wire with two `DKIM-Signature` headers. Both signers are constructed eagerly at create-time (configuration errors surface at boot, not at first send)."
2854
+ },
2855
+ {
2856
+ "title": "soc2-cc7 → soc2 posture rename",
2857
+ "body": "every guard's compliance posture name changes from `\"soc2-cc7\"` to `\"soc2\"`. The CC7-specific scoping was misleading (SOC 2 controls span CC1–CC9; the existing posture wasn't CC7-specific). Operators with `compliancePosture: \"soc2-cc7\"` MUST update to `compliancePosture: \"soc2\"` — the old name now throws `unknown compliance posture`."
2858
+ }
2859
+ ]
2860
+ }
2861
+ ],
2862
+ "references": [
2863
+ {
2864
+ "label": "RFC 8463",
2865
+ "url": "https://www.rfc-editor.org/rfc/rfc8463.html"
2866
+ }
2867
+ ]
2868
+ },
2869
+ {
2870
+ "version": "0.7.21",
2871
+ "date": "2026-05-05",
2872
+ "headline": "Small primitive batch (5 fixes): TLS 1",
2873
+ "summary": "3 framework-wide minimum, `b.safeRedirect`, `b.pick`, audit-sign legacy compat-shim removed, webhook PQC signatures emit base64url. Framework-wide TLS 1.3 minimum — `index.js` sets `tls.DEFAULT_MIN_VERSION = \"TLSv1.3\"` once at boot, before any framework module loads `node:tls`.",
2874
+ "sections": [
2875
+ {
2876
+ "heading": "Fixed",
2877
+ "items": [
2878
+ {
2879
+ "title": "Framework-wide TLS 1.3 minimum",
2880
+ "body": "`index.js` sets `tls.DEFAULT_MIN_VERSION = \"TLSv1.3\"` once at boot, before any framework module loads `node:tls`. Applies to every TLS socket (outbound `https.request` / mail SMTP+STARTTLS / Redis-Postgres-Mongo TLS / `b.httpClient`, AND inbound `https.createServer` when blamejs is the listener). Per-call override still works for legacy peers."
2881
+ },
2882
+ {
2883
+ "title": "`b.safeRedirect`",
2884
+ "body": "open-redirect (CWE-601) defense. `b.safeRedirect.resolve(rawTarget, { allowedOrigins, allowedHosts, fallback })` returns the safe URL (or fallback). Refuses protocol-relative (`//attacker.com`), backslash variants (`\\\\\\\\attacker.com`), control-char-laden (CRLF header injection), and `data:` / `javascript:` schemes; same-origin paths (`/dashboard`) and fragments (`#x`) pass through; full URLs require explicit allowlist. Operator drops the result straight into `res.writeHead(302, { Location: ... })`."
2885
+ },
2886
+ {
2887
+ "title": "`b.pick`",
2888
+ "body": "mass-assignment (CWE-915 / OWASP API3:2023) defense. `b.pick(req.body, [\"a\", \"b\", [\"nested\", [\"sub1\"]]])` returns a NEW object with only allowlisted keys; prototype-pollution keys (`__proto__` / `constructor` / `prototype`) ALWAYS stripped even if listed. `opts.onUnknown: \"throw\"` rejects unknown keys instead of silently dropping. Nested allowlist syntax for object-shaped fields."
2889
+ },
2890
+ {
2891
+ "title": "Audit-sign legacy compat-shim removed",
2892
+ "body": "`lib/audit-sign.js` no longer falls back to `ml-dsa-87` for key files missing the `algorithm` field. Throws `KEY_FILE_MISSING_ALG` / `UNWRAPPED_MISSING_ALG` at load time; operators with legacy files rotate the key (deletes + regenerates) or hand-edit to add `\"algorithm\": \"slh-dsa-shake-256f\"`. Pre-v1 compat-shim sweep per the no-pre-v1-compat rule."
2893
+ },
2894
+ {
2895
+ "title": "Webhook PQC signatures emit base64url",
2896
+ "body": "`b.webhook` now signs to base64url (was hex). SLH-DSA-SHAKE-256f signatures are ~29.5 KB binary → ~40 KB base64url vs ~59 KB hex; the hex form blew past nginx default 8 KB / Cloudflare default 16 KB / many CDN edge limits. Verification accepts EITHER encoding for a transition window — base64url-shaped sig values decode as base64url; hex-shaped values decode as hex."
2897
+ }
2898
+ ]
2899
+ }
2900
+ ]
2901
+ },
2902
+ {
2903
+ "version": "0.7.20",
2904
+ "date": "2026-05-05",
2905
+ "headline": "Browser-hardening batch (5 fixes): CSRF Origin/Referer cross-check, default CSP gains Trusted Types, expanded",
2906
+ "summary": "middleware.fetchMetadata`. CSRF Origin/Referer cross-check (`b.middleware.csrfProtect`) — second-line defense alongside the double-submit token. State-changing requests whose Origin (or Referer when Origin is absent) doesn't resolve to the request's own origin are refused before the token check.",
2907
+ "sections": [
2908
+ {
2909
+ "heading": "Fixed",
2910
+ "items": [
2911
+ {
2912
+ "title": "Expanded Permissions-Policy",
2913
+ "body": "defaults now disable `browsing-topics=()`, `attribution-reporting=()`, `unload=()`, `interest-cohort=()`, `join-ad-interest-group=()`, `run-ad-auction=()`, `private-state-token-issuance=()`, `private-state-token-redemption=()`, `compute-pressure=()`, `hid=()`, `serial=()`, `idle-detection=()` — closes advertising / tracking / load-event-leak / device-API surfaces. Existing operator overrides via `permissionsPolicy: \"...\"` continue unchanged."
2914
+ },
2915
+ {
2916
+ "title": "`__Host-` / `__Secure-` cookie prefix invariants",
2917
+ "body": "(`b.cookies.serialize`) — RFC 6265bis §4.1.3 enforced at serialize time. `__Secure-*` requires `secure: true`; `__Host-*` requires `secure: true` AND `path: \"/\"` AND no `domain:`. Each violation throws a typed `CookieError` with operator-actionable code (`cookies/prefix-secure-required`, `cookies/prefix-host-secure-required`, `cookies/prefix-host-path-required`, `cookies/prefix-host-no-domain`) instead of producing a malformed cookie that browsers silently reject."
2918
+ },
2919
+ {
2920
+ "title": "`b.middleware.fetchMetadata`",
2921
+ "body": "new fetch-metadata isolation primitive. Reads `Sec-Fetch-Site` / `Sec-Fetch-Mode` / `Sec-Fetch-Dest` and refuses cross-site state-changing requests by default. Operators opt in to specific destinations (e.g. `allowedDest: [\"empty\", \"document\"]`) or specific origins (`allowCrossSite: true`). Direct navigations (typed URL / bookmark — `Sec-Fetch-Site: none`) pass through; `same-origin` always passes; `same-site` configurable. Missing fetch-metadata (legacy browsers, server-to-server) deferred to other auth/CSRF layers per `allowMissing: true` default."
2922
+ }
2923
+ ]
2924
+ }
2925
+ ]
2926
+ },
2927
+ {
2928
+ "version": "0.7.19",
2929
+ "date": "2026-05-05",
2930
+ "headline": "Auth primitives — session timeouts, JWT keyResolver, bearer auth middleware, external JWT verify, Argon2id params",
2931
+ "summary": "Five auth-surface additions: session idle + absolute timeouts (`b.session.verify`) now enforce OWASP ASVS 5.0 §3.3 / NIST SP 800-63B-4 with defaults of 30 min idle and 12 hours absolute. JWT `keyResolver` closes the kid-rotation gap. `b.middleware.bearerAuth`, `b.auth.jwt.verifyExternal`, and `b.auth.password.params()` round out the additions.",
2932
+ "sections": [
2933
+ {
2934
+ "heading": "Added",
2935
+ "items": [
2936
+ {
2937
+ "title": "Session idle + absolute timeouts",
2938
+ "body": "`b.session.verify` now enforces OWASP ASVS 5.0 §3.3 / NIST SP 800-63B-4 timeouts — defaults: 30 min idle, 12 hours absolute. Operators opt out per-call by passing `idleTimeoutMs: 0` / `absoluteTimeoutMs: 0`. Both surface `auth.session.expired_idle` / `auth.session.expired_absolute` audit events on enforcement."
2939
+ },
2940
+ {
2941
+ "title": "JWT `keyResolver` opt for kid rotation",
2942
+ "body": "`b.auth.jwt.verify` accepts `keyResolver(decodedHeader)` to look up the public key per-token (typically by `kid`). Mutually exclusive with `opts.publicKey`. Async-friendly. Closes the kid-rotation gap where signers carried `kid` but verifiers only accepted a single static key."
2943
+ },
2944
+ {
2945
+ "title": "`b.middleware.bearerAuth`",
2946
+ "body": "New middleware that extracts `Authorization: Bearer <token>`, runs an operator-supplied `verify(token)` function, and attaches `req.user`. Distinct from cookie-session `attachUser`. Missing-Authorization passes through (so cookie path can take over); invalid/null/throw rejects 401 + `WWW-Authenticate: Bearer error=\"invalid_token\"` per RFC 6750 §3."
2947
+ },
2948
+ {
2949
+ "title": "`b.auth.jwt.verifyExternal`",
2950
+ "body": "new generic classical-alg JWT verifier (RS256 / RS384 / RS512 / PS256 / PS384 / PS512 / ES256 / ES384 / ES512 / EdDSA) for integration with external IdPs (Auth0 / Okta / Keycloak / Cognito / Azure AD / Google / Apple). `algorithms` is REQUIRED with no default — defends the alg-confusion class (CVE-2024-54150 / CVE-2025-30144 / CVE-2026-22817 Hono). HMAC algs and `none` are explicitly refused (HMAC + JWKS public-key trust source IS the alg-confusion vector). Three key-source options: `jwks` (pre-fetched array), `jwksUri` (auto-fetched + TTL-cached via `b.httpClient` SSRF gate), `keyResolver` (custom). Standard claim checks (`exp` / `nbf` / `iat` / `aud` / `iss` / `sub`) with operator-tunable `clockSkewMs`."
2951
+ },
2952
+ {
2953
+ "title": "`b.auth.password.params()`",
2954
+ "body": "new accessor returning the active Argon2id params (`memoryCostKib` / `timeCost` / `parallelism`) plus the OWASP 2026 floor (`19 MiB` / `t>=2` / `p>=1`) plus `meetsFloor: bool`. Compliance-audit visibility without parsing PHC strings."
2955
+ }
2956
+ ]
2957
+ }
2958
+ ],
2959
+ "references": [
2960
+ {
2961
+ "label": "RFC 6750",
2962
+ "url": "https://www.rfc-editor.org/rfc/rfc6750.html"
2963
+ },
2964
+ {
2965
+ "label": "CVE-2024-54150",
2966
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-54150"
2967
+ },
2968
+ {
2969
+ "label": "CVE-2025-30144",
2970
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-30144"
2971
+ },
2972
+ {
2973
+ "label": "CVE-2026-22817",
2974
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22817"
2975
+ }
2976
+ ]
2977
+ },
2978
+ {
2979
+ "version": "0.7.18",
2980
+ "date": "2026-05-05",
2981
+ "headline": "Transport-layer smuggling hardening (four ship-blocker fixes)",
2982
+ "summary": "HTTP request-smuggling defense in `b.middleware.bodyParser` per RFC 9112 §6.1: rejects requests with both `Content-Length` and `Transfer-Encoding` headers (CL.TE / TE.CL smuggling — CVE-2022-31394 / CVE-2024-27316 class), multiple `Content-Length` values, `Transfer-Encoding`.",
2983
+ "sections": [
2984
+ {
2985
+ "heading": "Fixed",
2986
+ "items": [
2987
+ {
2988
+ "title": "Transport-layer smuggling hardening (four ship-blocker fixes)",
2989
+ "body": "transport-layer smuggling hardening (four ship-blocker fixes). HTTP request-smuggling defense in `b.middleware.bodyParser` per RFC 9112 §6.1: rejects requests with both `Content-Length` and `Transfer-Encoding` headers (CL.TE / TE.CL smuggling — CVE-2022-31394 / CVE-2024-27316 class), multiple `Content-Length` values, `Transfer-Encoding` whose final coding is not `chunked`, and duplicate `chunked` tokens (TE.TE smuggling). Each rejection responds 400 + `Connection: close` so the upstream proxy doesn't reuse the socket. Static-serve symlink-escape + filename safety in `b.staticServe`: `_resolveSafe` now `fs.realpathSync`-es the resolved path (defeats symlink-out-of-root) AND validates the basename through `b.guardFilename` at the balanced profile (rejects path traversal, null-byte, NTFS alternate data streams, UNC paths, RTLO bidi, overlong UTF-8, Windows reserved device names, double-extension; balanced profile chosen over strict so legitimate operator-deposited shell-exec extensions like `.exe`/`.bin` remain serveable). Outbound SMTP smuggling defense in `b.mail` SMTP transport: every produced RFC 822 wire (post-DKIM-sign) is run through `b.guardEmail.validateMessage` at strict profile before the socket opens; refuses on critical issues — bare CR / bare LF + smuggled SMTP verbs (CVE-2023-51764 Postfix / CVE-2023-51765 Sendmail / CVE-2023-51766 Exim / CVE-2026-32178 .NET class) cannot leave the framework even when operator-supplied subject/body/headers contain the pattern. DKIM `l=` body-length tag forbidden in `b.mail.dkim.create`: passing `bodyLength` now throws `dkim/l-tag-forbidden` at create-time. M³AAWG / Gmail / Microsoft 365 guidance is \"never use l=\" — it enables append-after-signature attacks where an attacker appends arbitrary content past the signed length and the DKIM signature still validates against the original prefix. The body is always hashed in full."
2990
+ }
2991
+ ]
2992
+ }
2993
+ ],
2994
+ "references": [
2995
+ {
2996
+ "label": "RFC 9112",
2997
+ "url": "https://www.rfc-editor.org/rfc/rfc9112.html"
2998
+ },
2999
+ {
3000
+ "label": "RFC 822",
3001
+ "url": "https://www.rfc-editor.org/rfc/rfc822.html"
3002
+ },
3003
+ {
3004
+ "label": "CVE-2022-31394",
3005
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-31394"
3006
+ },
3007
+ {
3008
+ "label": "CVE-2024-27316",
3009
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-27316"
3010
+ },
3011
+ {
3012
+ "label": "CVE-2023-51764",
3013
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-51764"
3014
+ },
3015
+ {
3016
+ "label": "CVE-2023-51765",
3017
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-51765"
3018
+ },
3019
+ {
3020
+ "label": "CVE-2023-51766",
3021
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-51766"
3022
+ },
3023
+ {
3024
+ "label": "CVE-2026-32178",
3025
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32178"
3026
+ }
3027
+ ]
3028
+ },
3029
+ {
3030
+ "version": "0.7.17",
3031
+ "date": "2026-05-05",
3032
+ "headline": "`b.guardEmail` email content-safety primitive (single-address validation + full RFC 822 / 5322",
3033
+ "summary": "message validation). Threat catalog grounded in current research (SMTP smuggling — CVE-2023-51764 Postfix / CVE-2023-51765 Sendmail / CVE-2023-51766 Exim / CVE-2026-32178 .NET System.Net.Mail; SEC Consult / smtpsmuggling.com class; IDN homograph attacks; CRLF header injection).",
3034
+ "sections": [
3035
+ {
3036
+ "heading": "Added",
3037
+ "items": [
3038
+ {
3039
+ "title": "Profiles",
3040
+ "body": "strict / balanced / permissive (SMTP smuggling + CRLF header injection + multi-@ + null bytes refused at every profile — universal class)."
3041
+ },
3042
+ {
3043
+ "title": "Compliance postures",
3044
+ "body": "hipaa / pci-dss / gdpr / soc2-cc7. Strict default-on via v0.7.12: every `b.fileUpload` + `b.staticServe` deploy gets this gate at strict profile automatically."
3045
+ }
3046
+ ]
3047
+ }
3048
+ ],
3049
+ "references": [
3050
+ {
3051
+ "label": "RFC 822",
3052
+ "url": "https://www.rfc-editor.org/rfc/rfc822.html"
3053
+ },
3054
+ {
3055
+ "label": "RFC 5322",
3056
+ "url": "https://www.rfc-editor.org/rfc/rfc5322.html"
3057
+ },
3058
+ {
3059
+ "label": "RFC 5321",
3060
+ "url": "https://www.rfc-editor.org/rfc/rfc5321.html"
3061
+ },
3062
+ {
3063
+ "label": "CVE-2023-51764",
3064
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-51764"
3065
+ },
3066
+ {
3067
+ "label": "CVE-2023-51765",
3068
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-51765"
3069
+ },
3070
+ {
3071
+ "label": "CVE-2023-51766",
3072
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-51766"
3073
+ },
3074
+ {
3075
+ "label": "CVE-2026-32178",
3076
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32178"
3077
+ }
3078
+ ]
3079
+ },
3080
+ {
3081
+ "version": "0.7.16",
3082
+ "date": "2026-05-05",
3083
+ "headline": "`b.guardMarkdown` markdown content-safety primitive. Threat catalog grounded in current research",
3084
+ "summary": "(CVE-2026-30838 CommonMark DisallowedRawHtml whitespace-tag bypass; CVE-2025-9540 Markup Markdown javascript: link XSS; CVE-2025-7969 markdown-it ReDoS; CVE-2025-6493 CodeMirror Markdown catastrophic backtracking; CVE-2025-24981 MDC autolink XSS; CVE-2026-33500 AVideo Parsedown l.",
3085
+ "sections": [
3086
+ {
3087
+ "heading": "Added",
3088
+ "items": [
3089
+ {
3090
+ "title": "Profiles",
3091
+ "body": "strict / balanced / permissive (dangerous tags + dangerous schemes + image schemes + autolink schemes refused at every profile — script-tag and javascript: are universal class)."
3092
+ },
3093
+ {
3094
+ "title": "Compliance postures",
3095
+ "body": "hipaa / pci-dss / gdpr / soc2-cc7. Strict default-on via v0.7.12: every `b.fileUpload` + `b.staticServe` deploy gets this gate at strict profile automatically."
3096
+ }
3097
+ ]
3098
+ }
3099
+ ],
3100
+ "references": [
3101
+ {
3102
+ "label": "CVE-2026-30838",
3103
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30838"
3104
+ },
3105
+ {
3106
+ "label": "CVE-2025-9540",
3107
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-9540"
3108
+ },
3109
+ {
3110
+ "label": "CVE-2025-7969",
3111
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-7969"
3112
+ },
3113
+ {
3114
+ "label": "CVE-2025-6493",
3115
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-6493"
3116
+ },
3117
+ {
3118
+ "label": "CVE-2025-24981",
3119
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24981"
3120
+ },
3121
+ {
3122
+ "label": "CVE-2026-33500",
3123
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33500"
3124
+ }
3125
+ ]
3126
+ },
3127
+ {
3128
+ "version": "0.7.15",
3129
+ "date": "2026-05-04",
3130
+ "headline": "`b.guardXml` XML content-safety primitive + smoke parallel mode + persistent test output",
3131
+ "summary": ". `b.guardXml` threat catalog grounded in current research (CVE-2026-24400 AssertJ XXE; CVE-2025-3225 sitemap parser; CVE-2024-1455 LangChain; CVE-2024-25062 libxml2 use-after-free with DTD + XInclude; CVE-2024-56171 + CVE-2025-24928 + CVE-2025-32415 + CVE-2025-27113 libxml2 family; CVE-2024-8176 libexpat stack overflow via recursive entity expansion).",
3132
+ "sections": [
3133
+ {
3134
+ "heading": "Added",
3135
+ "items": [
3136
+ {
3137
+ "title": "`b.guardXml`",
3138
+ "body": "threat catalog grounded in current research (CVE-2026-24400 AssertJ XXE; CVE-2025-3225 sitemap parser; CVE-2024-1455 LangChain; CVE-2024-25062 libxml2 use-after-free with DTD + XInclude; CVE-2024-56171 + CVE-2025-24928 + CVE-2025-32415 + CVE-2025-27113 libxml2 family; CVE-2024-8176 libexpat stack overflow via recursive entity expansion). Surface: `validate(input, opts)` returns `{ ok, issues }`; `sanitize(input, opts)` strips character-class threats but throws on critical (DOCTYPE / ENTITY / external — no safe sanitization); `gate(opts)` returns a `b.gateContract`-shaped gate auto-routed by `b.guardAll` for `application/xml` / `text/xml`. KIND=\"content\". Threat catalog: DOCTYPE refused unconditionally (XXE / billion-laughs vector); `<!ENTITY>` declarations including parameter entities (`%` prefix); external entity references (`SYSTEM`/`PUBLIC` with file://, http://, etc.); XInclude (`<xi:include>`); `xsi:schemaLocation` schema fetch; processing instructions (skipping standard `<?xml?>` declaration); CDATA sections; XML signature wrapping (audit); bidi/null/control/zero-width; element-count + depth caps; per-attribute-value-length cap."
3139
+ },
3140
+ {
3141
+ "title": "Profiles",
3142
+ "body": "strict / balanced / permissive (DOCTYPE refused at all profile levels — billion-laughs class is universal)."
3143
+ },
3144
+ {
3145
+ "title": "Compliance postures",
3146
+ "body": "hipaa / pci-dss / gdpr / soc2-cc7."
3147
+ },
3148
+ {
3149
+ "title": "Smoke parallel mode",
3150
+ "body": "new `SMOKE_PARALLEL=N` env var forks Layer 0 test files in parallel batches (each fork is a fresh Node child process for module-state isolation). Layers 1-5 stay sequential because they share db / cluster / vault state. Sanity ceiling 64. Empirical: SMOKE_PARALLEL=64 takes 91s vs sequential 122s (25% faster)."
3151
+ },
3152
+ {
3153
+ "title": "Persistent test output",
3154
+ "body": "`test/smoke.js`, `test/layer-0-primitives/codebase-patterns.test.js` (CLI mode), and `examples/wiki/test/e2e.js` now write a tee'd copy of all stdout/stderr to `.test-output/smoke.log` / `.test-output/codebase-patterns.log` / `.test-output/wiki-e2e.log`. Tee semantics — original stdout/stderr passthrough preserved so npm exit codes + GitHub Actions annotations work unchanged. `.test-output/` is auto-gitignored via the existing `.* ` catchall and never shipped (npm `files` allowlist explicit)."
3155
+ },
3156
+ {
3157
+ "title": "Family infrastructure",
3158
+ "body": "guard-xml shares the same family-ABI shape (resolveProfileAndPosture / aggregateIssues / buildGuardGate / extractBytesAsText / etc.) as the other 6 family members; the v0.7.13 `family-subset` cluster allowlist sustains."
3159
+ }
3160
+ ]
3161
+ }
3162
+ ],
3163
+ "references": [
3164
+ {
3165
+ "label": "CVE-2026-24400",
3166
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-24400"
3167
+ },
3168
+ {
3169
+ "label": "CVE-2025-3225",
3170
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-3225"
3171
+ },
3172
+ {
3173
+ "label": "CVE-2024-1455",
3174
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1455"
3175
+ },
3176
+ {
3177
+ "label": "CVE-2024-25062",
3178
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-25062"
3179
+ },
3180
+ {
3181
+ "label": "CVE-2024-56171",
3182
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-56171"
3183
+ },
3184
+ {
3185
+ "label": "CVE-2025-24928",
3186
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24928"
3187
+ },
3188
+ {
3189
+ "label": "CVE-2025-32415",
3190
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-32415"
3191
+ },
3192
+ {
3193
+ "label": "CVE-2025-27113",
3194
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-27113"
3195
+ },
3196
+ {
3197
+ "label": "CVE-2024-8176",
3198
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-8176"
3199
+ }
3200
+ ]
3201
+ },
3202
+ {
3203
+ "version": "0.7.14",
3204
+ "date": "2026-05-04",
3205
+ "headline": "`b.guardYaml` YAML content-safety primitive. Threat catalog grounded in current research",
3206
+ "summary": "(CVE-2026-24009 Docling/PyYAML unsafe load → RCE; CVE-2026-27807 MarkUs alias billion-laughs DoS; CVE-2025-68664 LangChain deserialization → RCE; CVE-2025-61301 + CVE-2025-61303 YAML library DoS family — \"Laughter in the Wild\" study; CVE-2022-1471 SnakeYAML constructor RCE; CVE-2.",
3207
+ "sections": [
3208
+ {
3209
+ "heading": "Added",
3210
+ "items": [
3211
+ {
3212
+ "title": "Profiles",
3213
+ "body": "strict (refuse every threat; 2 MiB cap; depth 8; 16 anchors; 1 alias depth; 1 document; 1024 nodes); balanced (audit most threats; allow YAML 1.2 core tags `!!str` / `!!int` / `!!seq` / `!!map` / etc.; refuse alias-explosion regardless; 8 MiB cap; 64 anchors); permissive (audit most; refuse only null bytes + alias-explosion; 64 MiB cap; 1024 anchors)."
3214
+ },
3215
+ {
3216
+ "title": "Compliance postures",
3217
+ "body": "hipaa / pci-dss / gdpr / soc2-cc7 with strict overlays + forensic snapshots. Refuse-only sanitize discipline: YAML has no safe sanitization — gate decisions are serve / audit-only / refuse only. Strict default-on via v0.7.12: every `b.fileUpload` + `b.staticServe` deploy gets this gate at strict profile automatically."
3218
+ }
3219
+ ]
3220
+ }
3221
+ ],
3222
+ "references": [
3223
+ {
3224
+ "label": "CVE-2026-24009",
3225
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-24009"
3226
+ },
3227
+ {
3228
+ "label": "CVE-2026-27807",
3229
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27807"
3230
+ },
3231
+ {
3232
+ "label": "CVE-2025-68664",
3233
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-68664"
3234
+ },
3235
+ {
3236
+ "label": "CVE-2025-61301",
3237
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-61301"
3238
+ },
3239
+ {
3240
+ "label": "CVE-2025-61303",
3241
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-61303"
3242
+ },
3243
+ {
3244
+ "label": "CVE-2022-1471",
3245
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-1471"
3246
+ },
3247
+ {
3248
+ "label": "CVE-2020-1747",
3249
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-1747"
3250
+ },
3251
+ {
3252
+ "label": "CVE-2020-14343",
3253
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-14343"
3254
+ },
3255
+ {
3256
+ "label": "CVE-2017-18342",
3257
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-18342"
3258
+ }
3259
+ ]
3260
+ },
3261
+ {
3262
+ "version": "0.7.13",
3263
+ "date": "2026-05-04",
3264
+ "headline": "`b.guardJson` JSON content-safety primitive. Threat catalog grounded in current research",
3265
+ "summary": "(CVE-2025-55182 React/Next.js Server Functions deserialization → RCE; CVE-2025-57820 + CVE-2026-30226 Svelte devalue prototype pollution; CVE-2026-35209 defu; CVE-2026-28794 @orpc/client; CVE-2025-13465 Lodash; CVE-2025-25014 Kibana RCE; CVE-2024-38984 json-override; CVE-2022-42743 deep-parse-json; GHSA-9c47-m6qq-7p4h JSON5).",
3266
+ "sections": [
3267
+ {
3268
+ "heading": "Added",
3269
+ "items": [
3270
+ {
3271
+ "title": "Threat catalog",
3272
+ "body": "source-level prototype-pollution detection (catches `__proto__` / `constructor` / `prototype` keys BEFORE parse — JSON.parse silently routes `__proto__` through the prototype setter so a post-parse Object.keys walk misses the most dangerous key in the catalog); duplicate-key detection (RFC 8259 SHOULD-unique violation; JSON.parse silently last-wins, letting attackers smuggle duplicate-key payloads past validation that ran on the first occurrence — Bishop Fox JSON-interoperability research); NaN / Infinity / -Infinity / undefined refusal (RFC 8259 forbids; JSON5 / lenient libraries accept); comment refusal (single-line and block); trailing-comma refusal; JSON5 syntax refusal (single-quoted keys, hex literals, unquoted keys); BOM injection (leading + mid-stream); bidi / null / control / zero-width chars; numeric precision-loss (integers above `Number.MAX_SAFE_INTEGER`); top-level-key allowlist; depth + breadth + array-length + string-length + node-count caps."
3273
+ },
3274
+ {
3275
+ "title": "Profiles",
3276
+ "body": "strict (refuse every threat; 2 MiB doc cap; depth 8; 256 keys/object; 1024 array items) / balanced (strip pollution + BOM + bidi + control + null + zero-width; audit duplicates + comments + trailing commas + JSON5 + numeric precision; refuse NaN; 8 MiB cap; depth 32) / permissive (audit most; refuse only null bytes outright; 64 MiB cap; depth 64)."
3277
+ },
3278
+ {
3279
+ "title": "Compliance postures",
3280
+ "body": "hipaa / pci-dss / gdpr / soc2-cc7 with strict overlays + forensic snapshots. Strict default-on via v0.7.12: every `b.fileUpload` + `b.staticServe` deploy gets this gate at strict profile automatically — no explicit wiring required. Family infrastructure: `KNOWN_CLUSTERS` allowlist gains `mode: \"family-subset\"` matcher (collapses 6+ exact-match cluster entries into one subset entry; sustainable as the family grows beyond 6 guards)."
3281
+ }
3282
+ ]
3283
+ }
3284
+ ],
3285
+ "references": [
3286
+ {
3287
+ "label": "RFC 8259",
3288
+ "url": "https://www.rfc-editor.org/rfc/rfc8259.html"
3289
+ },
3290
+ {
3291
+ "label": "CVE-2025-55182",
3292
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-55182"
3293
+ },
3294
+ {
3295
+ "label": "CVE-2025-57820",
3296
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-57820"
3297
+ },
3298
+ {
3299
+ "label": "CVE-2026-30226",
3300
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30226"
3301
+ },
3302
+ {
3303
+ "label": "CVE-2026-35209",
3304
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35209"
3305
+ },
3306
+ {
3307
+ "label": "CVE-2026-28794",
3308
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28794"
3309
+ },
3310
+ {
3311
+ "label": "CVE-2025-13465",
3312
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-13465"
3313
+ },
3314
+ {
3315
+ "label": "CVE-2025-25014",
3316
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-25014"
3317
+ },
3318
+ {
3319
+ "label": "CVE-2024-38984",
3320
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-38984"
3321
+ },
3322
+ {
3323
+ "label": "CVE-2022-42743",
3324
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-42743"
3325
+ }
3326
+ ]
3327
+ },
3328
+ {
3329
+ "version": "0.7.12",
3330
+ "date": "2026-05-04",
3331
+ "headline": "`b.fileUpload` and `b.staticServe` ship with `b.guardAll` wired ON by default at strict profile",
3332
+ "summary": ". Defense-in-depth applied automatically: every operator-supplied bytes path runs through the full guard-* family without any explicit wiring.",
3333
+ "sections": [
3334
+ {
3335
+ "heading": "Added",
3336
+ "items": [
3337
+ {
3338
+ "title": "`b.fileUpload`",
3339
+ "body": "`contentSafety` defaults to `b.guardAll.byExtension({ profile: \"strict\" })` (every shipped guard's full threat catalog refused — dangerous tags, event handlers, dangerous URL schemes, formula injection, DOCTYPE, SVGZ, animation-href hijack, zip-slip, ratio bombs); new `filenameSafety` opt defaults to `b.guardFilename.gate({ profile: \"strict\" })` (path traversal + null-byte truncation + Windows reserved names + NTFS ADS + RTLO bidi + overlong UTF-8 + shell-exec extensions + double-extension + reserved chars)."
3340
+ },
3341
+ {
3342
+ "title": "`b.staticServe`",
3343
+ "body": "`contentSafety` defaults to `b.guardAll.byExtension({ profile: \"strict\" })`. Explicit opt-out: `contentSafety: null` / `filenameSafety: null` skips the gate AND emits an audit row at `create()` time (`fileUpload.contentSafety.disabled` / `fileUpload.filenameSafety.disabled` / `staticServe.contentSafety.disabled`) with operator-supplied `contentSafetyDisabledReason` / `filenameSafetyDisabledReason` metadata so a security review can reconstruct which deploys disabled the default-on protection AND why. filenameSafety wiring: runs in `fileUpload.finalize` BEFORE `contentSafety` (a refused filename obviates body validation); honours sanitize action (replaces `metadata.filename` with the cleaned form so downstream code sees the safe name). Explicit operator wiring still works: passing `contentSafety: { \".csv\": gate }` or `filenameSafety: gate` uses the operator-supplied object as-is (no default-on override). Migration: this is a behavior change. Code that called `b.fileUpload.create({ stagingDir, onFinalize })` previously had no content / filename safety; after v0.7.12 the same call has both gates wired at strict profile. If existing routes depended on accepting hostile content (raw-bytes uploads, executable-extension artifacts), pass `contentSafety: null` and/or `filenameSafety: null` with an operator-supplied reason at `create()` time."
3344
+ }
3345
+ ]
3346
+ }
3347
+ ]
3348
+ },
3349
+ {
3350
+ "version": "0.7.11",
3351
+ "date": "2026-05-04",
3352
+ "headline": "Adaptive guard-* family integration harness",
3353
+ "summary": "`test/layer-5-integration/guard-host-integration.test.js` discovers every guard primitive registered in `b.guardAll.allGuards()` and exercises gate decisions through the appropriate host wiring per guard's `KIND`. Adding a new guard automatically picks up the harness without touching the test file.",
3354
+ "sections": [
3355
+ {
3356
+ "heading": "Added",
3357
+ "items": [
3358
+ {
3359
+ "title": "Adaptive design",
3360
+ "body": "adding a new guard automatically picks up the harness without touching the test file. Each guard exports `KIND` (`\"content\"` / `\"entries\"` / `\"filename\"`) and `INTEGRATION_FIXTURES` with kind-appropriate sample payloads (benign + hostile). The harness iterates `b.guardAll.allGuards()`, dispatches per kind, and per-guard runs: direct gate (benign → serve; hostile → not serve); `b.guardAll.gate` contentTypeMux dispatch; `b.guardAll.gate({ exceptFor })` opt-out path with audit-row verification of the skipped roster; `b.staticServe.create({ contentSafety })` GET round-trip (benign → 2xx, hostile → 4xx); `b.fileUpload.create({ contentSafety })` chunk → finalize round-trip (benign succeeds, hostile throws content-safety error); audit chain captures host-level rows."
3361
+ },
3362
+ {
3363
+ "title": "Family additions",
3364
+ "body": "`lib/guard-all.js` gains `STANDALONE_GUARDS` array (currently `[guardFilename]`) and `allGuards()` aggregator that returns `GUARDS.concat(STANDALONE_GUARDS)`. Each guard module exports `KIND` + `INTEGRATION_FIXTURES`."
3365
+ },
3366
+ {
3367
+ "title": "Tests run in-process",
3368
+ "body": "no docker / network / fixture-archive dependencies; the harness completes in ~100ms regardless of guard count, so the gate runs on every smoke."
3369
+ }
3370
+ ]
3371
+ }
3372
+ ]
3373
+ },
3374
+ {
3375
+ "version": "0.7.10",
3376
+ "date": "2026-05-04",
3377
+ "headline": "`b.guardArchive` archive content-safety primitive. Threat catalog grounded in current",
3378
+ "summary": "archive-extraction CVE research: CVE-2025-3445 mholt/archiver Zip Slip; CVE-2025-32779 EDDI Zip Slip; CVE-2025-62156 Argo Workflows Zip Slip; CVE-2025-66945 Zdir Pro path traversal; CVE-2025-45582 GNU Tar two-step symlink bypass; CVE-2025-11001/11002 7-Zip symlink + directory tra.",
3379
+ "sections": [
3380
+ {
3381
+ "heading": "Added",
3382
+ "items": [
3383
+ {
3384
+ "title": "Tests",
3385
+ "body": "102 new layer-0 assertions covering surface, registry parity, zip-slip detection (3 forms), absolute-path detection (3 forms), symlink reject (strict), symlink escape (balanced), hardlink reject + escape, compression-ratio bomb (per-entry), aggregate-ratio bomb, total-size cap, entry-count cap, nested-archive (.zip / .tar.gz), duplicate-entry-name, case-insensitive collision, encryption-claim mismatch, sparse entry, magic-byte detection (8 formats including tar via offset-257 ustar), checkExtractionPath helper, clean-archive + warn-only handling, gate decision shapes (clean / refuse / no-entry-list), profile + posture vocabulary."
3386
+ }
3387
+ ]
3388
+ }
3389
+ ],
3390
+ "references": [
3391
+ {
3392
+ "label": "CVE-2025-3445",
3393
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-3445"
3394
+ },
3395
+ {
3396
+ "label": "CVE-2025-32779",
3397
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-32779"
3398
+ },
3399
+ {
3400
+ "label": "CVE-2025-62156",
3401
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62156"
3402
+ },
3403
+ {
3404
+ "label": "CVE-2025-66945",
3405
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-66945"
3406
+ },
3407
+ {
3408
+ "label": "CVE-2025-45582",
3409
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-45582"
3410
+ },
3411
+ {
3412
+ "label": "CVE-2025-11001",
3413
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-11001"
3414
+ },
3415
+ {
3416
+ "label": "CVE-2025-4138",
3417
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-4138"
3418
+ },
3419
+ {
3420
+ "label": "CVE-2025-10854",
3421
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-10854"
3422
+ },
3423
+ {
3424
+ "label": "CVE-2025-12060",
3425
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-12060"
3426
+ },
3427
+ {
3428
+ "label": "CVE-2026-26960",
3429
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-26960"
3430
+ }
3431
+ ]
3432
+ },
3433
+ {
3434
+ "version": "0.7.9",
3435
+ "date": "2026-05-04",
3436
+ "headline": "`b.guardFilename` filename content-safety primitive. Standalone primitive — does NOT register",
3437
+ "summary": "ister into `b.guardAll`'s content-type-routed dispatch (filename is a different axis from content-bytes; operators apply both — filename safety on the upload's name plus content safety on the body).",
3438
+ "sections": [
3439
+ {
3440
+ "heading": "Added",
3441
+ "items": [
3442
+ {
3443
+ "title": "Standalone primitive",
3444
+ "body": "does NOT register into `b.guardAll`'s content-type-routed dispatch (filename is a different axis from content-bytes; operators apply both — filename safety on the upload's name plus content safety on the body). Threat catalog grounded in OWASP Path Traversal + WSTG file-inclusion testing guides; CWE-22 / CWE-23 / CWE-35 / CWE-73 / CWE-78 / CWE-434; PortSwigger File-path-traversal series (null-byte bypass + extension validation); Memento-RTLO + RTL-Spiegel file-name spoofing reports (CVE-2021-42574 in filename context); Kevin Boone overlong UTF-8 sequence write-up."
3445
+ },
3446
+ {
3447
+ "title": "Threat catalog",
3448
+ "body": "path traversal (raw + percent-encoded `%2e%2e` + double-encoded `%252e%252e` + UTF-8 overlong `0xC0 0xAE` for dot and `0xC0 0xAF` for slash); null-byte truncation; Windows reserved device names (CON / PRN / AUX / NUL / COM1-9 / LPT1-9 / CLOCK$ / CONFIG$, with and without extensions, case-insensitive); NTFS alternate data streams (`name:stream`); leading/trailing whitespace + trailing dots (Windows silently strips them); Unicode bidi / RTLO file-name spoofing; zero-width / homoglyph chars; reserved characters (Windows < > : \" | ? *); UNC paths; path separators in leaf-name; length caps (strict 64-byte / balanced+permissive 255-byte); multi-dot policy (strict requires single dot, balanced+permissive allow .tar.gz); extension allowlist (catches double-extension bypass: `file.jpg.exe` matches `.exe` against the allowlist); shell-shortcut + executable extensions (.exe / .bat / .vbs / .ps1 / .lnk / .scr / .dll / .so / .dmg / .msi / etc); overlong UTF-8 detection at the Buffer level (RFC 3629 §3 prohibits non-shortest-form)."
3449
+ },
3450
+ {
3451
+ "title": "Profiles",
3452
+ "body": "strict (ASCII-only, single dot, single leaf, 64-byte cap, every threat class refused), balanced (Unicode NFC-normalized, multi-dot allowed, 255-byte cap, refuses dangerous classes + strips zero-width + audits homoglyphs), permissive (multi-component paths up to 16 components, reserved-name audited not refused for non-Windows targets)."
3453
+ },
3454
+ {
3455
+ "title": "Compliance postures",
3456
+ "body": "hipaa / pci-dss / gdpr / soc2-cc7 with appropriate strict overlays + ASCII-only + forensic snapshots."
3457
+ },
3458
+ {
3459
+ "title": "Sanitize discipline",
3460
+ "body": "strips leading/trailing whitespace + trailing dots, NFC-normalizes Unicode, replaces reserved chars with underscore (under strip policy), prepends underscore to Windows reserved device names. ALWAYS THROWS on path-traversal / null-byte / NTFS ADS / UNC / overlong UTF-8 — these have no safe sanitization, the only correct response is reject."
3461
+ },
3462
+ {
3463
+ "title": "New shared helpers",
3464
+ "body": "`gateContract.badInputResultIfNotStringOrBuffer(input)` and `gateContract.aggregateIssues(issues)` — extracted across guard-svg / guard-filename validate paths that need raw-Buffer input pre-conversion. Both registered in KNOWN_ANTIPATTERNS so future re-implementations fail the n=1 gate."
3465
+ },
3466
+ {
3467
+ "title": "Tests",
3468
+ "body": "79 new layer-0 assertions covering surface, path traversal (5 forms), percent-encoded traversal (3 forms), null-byte truncation, every Windows reserved name (12 cases including extensions), NTFS ADS, leading/trailing strip, RTLO bidi spoofing, reserved chars (7 cases), UNC paths, path separators in leaf, length cap, single-dot policy, extension allowlist, shell-exec extensions (11 cases), double-extension bypass, overlong UTF-8 at buffer level, ASCII-only enforcement, sanitize round-trip + throw-on-traversal/null-byte, gate decision shapes, profile + posture vocabulary."
3469
+ }
3470
+ ]
3471
+ }
3472
+ ],
3473
+ "references": [
3474
+ {
3475
+ "label": "RFC 3629",
3476
+ "url": "https://www.rfc-editor.org/rfc/rfc3629.html"
3477
+ },
3478
+ {
3479
+ "label": "CVE-2021-42574",
3480
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-42574"
3481
+ }
3482
+ ]
3483
+ },
3484
+ {
3485
+ "version": "0.7.8",
3486
+ "date": "2026-05-04",
3487
+ "headline": "`b.guardSvg` SVG content-safety primitive + guard-* family helper extraction. `b.guardSvg`",
3488
+ "summary": "vg` ships full v1 scope grounded in current SVG attack-surface research (Fortinet anatomy of SVG attack surface; Angular GHSA-jrmj-c5cx-3cw6 + GHSA-v4hv-rgfq-gp49 SVG animation/href XSS; SVGO CVE-2026-29074 billion laughs DoS; siyuan-note GHSA-5hc8-qmg8-pw27 animate-element san.",
3489
+ "sections": [
3490
+ {
3491
+ "heading": "Added",
3492
+ "items": [
3493
+ {
3494
+ "title": "`b.guardSvg`",
3495
+ "body": "ships full v1 scope grounded in current SVG attack-surface research (Fortinet anatomy of SVG attack surface; Angular GHSA-jrmj-c5cx-3cw6 + GHSA-v4hv-rgfq-gp49 SVG animation/href XSS; SVGO CVE-2026-29074 billion laughs DoS; siyuan-note GHSA-5hc8-qmg8-pw27 animate-element sanitizer bypass; cure53/DOMPurify issue #233 xlink:href filtering; insertScript SVG fun-time series). Threat catalog: dangerous tags (script / foreignObject / handler / iframe / embed / object / audio / video / animation family); SMIL animation attributeName allowlist enforcement (recent CVE class — `<animate attributeName=\"href\" to=\"javascript:...\"/>` is the published bypass shape, refused regardless of broader animation policy); on* + SMIL event-handler attributes (onclick / onerror / onload / onbegin / onend / onrepeat); href + xlink:href dangerous URL schemes (javascript / vbscript / data outside image-context / file / mhtml / view-source — entity-encoded form too); cross-origin `<use>` external refs (SSRF + XSS chain); XML DOCTYPE declarations refused unconditionally (defends billion-laughs entity expansion + XXE); custom `<!ENTITY>` declarations; CDATA + processing instructions; SVGZ compressed payloads (gzip magic bytes 0x1F 0x8B refused at gate level — operator must ungzip first); CSS injection in style attributes; <use>-recursion DoS via maxUseDepth; total-document size + element-count + attribute-count caps. Profiles: strict (minimal text+shapes; no animation; no external refs), balanced (adds <use> + <image> with same-origin or http(s); allowExternalRefs=true), permissive (adds animation with attributeName allowlist still enforced). Compliance postures: hipaa / pci-dss / gdpr / soc2-cc7 with strict overlays + forensic snapshots. Guard-* family helper extraction — five new shared helpers landed in `lib/codepoint-class.js` and `lib/gate-contract.js` and the existing guards refactored to consume them: `codepointClass.detectCharThreats(text, opts, codePrefix)`, `codepointClass.assertNoCharThreats(text, opts, errorFactory, codePrefix)`, `codepointClass.applyCharStripPolicies(text, opts)`, `gateContract.resolveProfileAndPosture(opts, cfg)`, `gateContract.runIssueValidator(input, opts, detector)`, `gateContract.buildGuardGate(name, opts, check)`, `gateContract.extractBytesAsText(ctx)`, `gateContract.lookupCompliancePosture(name, postures, errorFactory, codePrefix)`, `gateContract.makeRulePackLoader(errorClass, codePrefix)`, `gateContract.makeProfileBuilder(profiles)`, `numericBounds.requireAllPositiveFiniteIntIfPresent(opts, names, labelPrefix, errorClass, code)`. Pre-existing call sites in `lib/csv.js` and `lib/file-upload.js` migrated in the same patch per the no-future-patch-deferrals rule. New shared module `lib/codepoint-class.js` centralizes BIDI / C0_CTRL / ZERO_WIDTH range tables plus the `_hex4` / `_charClass` / `_fromCp` rendering helpers — the codepoint catalog has a single source of truth and future guard-* slices consume the shared module instead of redeclaring the tables. Each helper extraction registers its inline-shape in `KNOWN_ANTIPATTERNS` (the codebase-patterns gate fires at n=1 if any future code re-introduces the pattern)."
3496
+ },
3497
+ {
3498
+ "title": "Tests",
3499
+ "body": "83 new layer-0 assertions covering surface, registry parity, dangerous-tag detection, on* handler family, dangerous URL schemes (entity-encoded form too), animation attributeName href hijack, cross-origin <use> refusal, DOCTYPE / <!ENTITY> rejection, CDATA + processing-instruction policy, SVGZ magic-byte detection, CSS injection, bidi/control/null-byte detection, depth/size caps, sanitize round-trips, gate decision shapes, profile + posture vocabulary."
3500
+ }
3501
+ ]
3502
+ }
3503
+ ],
3504
+ "references": [
3505
+ {
3506
+ "label": "CVE-2026-29074",
3507
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-29074"
3508
+ }
3509
+ ]
3510
+ },
3511
+ {
3512
+ "version": "0.7.7",
3513
+ "date": "2026-05-04",
3514
+ "headline": "`b.guardHtml` HTML content-safety primitive ships full v1 scope. Threat catalog grounded in current",
3515
+ "summary": "sanitizer research (DOMPurify CVE series, OWASP XSS / DOM-Clobbering / HTML5 Security cheat sheets, PortSwigger and Sonar mXSS write-ups, html5sec.org).",
3516
+ "sections": [
3517
+ {
3518
+ "heading": "Added",
3519
+ "items": [
3520
+ {
3521
+ "title": "Threat catalog covered",
3522
+ "body": "dangerous tags (script / style / link / meta / base / iframe / object / embed / applet / form / input / button / frame / frameset / marquee / blink / plaintext / xmp / audio / video / source / track / math / svg / template / noscript / portal / dialog / keygen / menuitem / command); every `on*` event-handler attribute (caught family-wide via `/^on[a-z]/` regex — covers the entire HTML5 event-handler family without a manual list that rots when WHATWG specs a new event); form-override attributes (formaction / formmethod / formenctype / formtarget / formnovalidate, CWE-1021); iframe `srcdoc`; custom-element `is`; CSP-bypass-shaped attributes (nonce / integrity / crossorigin / http-equiv / manifest); URL-scheme allowlist with entity-decode pre-pass (defends `&#x6A;avascript:` and decimal-entity bypasses); CSS injection in style attribute values (expression / behavior: / -moz-binding / javascript:/vbscript: inside url() / @import / @namespace); DOM clobbering (id/name attribute values matching well-known JS globals on form/input/button/a/img/iframe/object/embed/select/textarea); mXSS hints (svg/math namespace-context-shift parents); IE conditional comments; Unicode bidi override (CVE-2021-42574 Trojan Source) / C0 control chars / null bytes / zero-width chars; tag-depth + attribute-count + per-attribute-value-size + total-document-size DoS caps."
3523
+ },
3524
+ {
3525
+ "title": "Profiles",
3526
+ "body": "strict (minimal text-formatting allowlist; reject every threat class) / balanced (links + images + tables + semantic markup; strip rather than reject for character-class threats; data:image/* on `<img>`) / permissive (every tag NOT in the dangerous-tag denylist)."
3527
+ },
3528
+ {
3529
+ "title": "Compliance postures",
3530
+ "body": "hipaa / pci-dss / gdpr / soc2-cc7 with appropriate strict/balanced overlays + forensic-snippet sizing (256 / 256 / 128 / 512 bytes)."
3531
+ },
3532
+ {
3533
+ "title": "Sanitizer discipline",
3534
+ "body": "drops dangerous tags AND their text-content body (script/style/iframe/object/embed/applet/template/svg/math etc. — body parsed as code in the host); strips on* attributes, dangerous URL schemes, CSS injection patterns, DOM-clobbering values, doctypes, CDATA, comments. Documented operator-facing trade-off: for hostile sources, validate+reject is the strong path; sanitize is best-effort and operators displaying untrusted HTML should additionally serve under a strict CSP."
3535
+ },
3536
+ {
3537
+ "title": "Codepoint-table programmatic regex pattern",
3538
+ "body": "same as guard-csv: BIDI_RANGES / C0_CTRL_RANGES / ZERO_WIDTH_RANGES literal numeric tables compiled into character classes via `_charClass` + `\\\\uXXXX` escapes at module load. Source file never embeds attack chars themselves."
3539
+ },
3540
+ {
3541
+ "title": "Tests",
3542
+ "body": "134 new layer-0 assertions covering surface, registry parity, every dangerous tag in the denylist, 15 representative on* handlers, 9 dangerous attributes, 10 dangerous URL schemes (entity-encoded form too), 5 CSS injection patterns, 8 DOM clobber globals, mXSS hints, IE conditional comments, bidi/control/null detection, depth/size caps, sanitize round-trips, escape correctness, gate decision shapes (clean/refuse/sanitize), profile + posture vocabulary."
3543
+ }
3544
+ ]
3545
+ }
3546
+ ],
3547
+ "references": [
3548
+ {
3549
+ "label": "CVE-2021-42574",
3550
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-42574"
3551
+ }
3552
+ ]
3553
+ },
3554
+ {
3555
+ "version": "0.7.6",
3556
+ "date": "2026-05-04",
3557
+ "headline": "`b.guardAll` registry + aggregator for the guard-* family. Security-on-by-default: every shipped",
3558
+ "summary": "guard is ON unless the operator opts OUT specifically with an audited reason.",
3559
+ "sections": [
3560
+ {
3561
+ "heading": "Added",
3562
+ "items": [
3563
+ {
3564
+ "title": "Surface",
3565
+ "body": "`b.guardAll.gate(opts)` returns a single `b.gateContract`-shaped gate that routes by `Content-Type` to the right registered guard; `b.guardAll.byExtension(opts)` and `b.guardAll.byContentType(opts)` return ready-made gate maps for direct drop-in to `b.staticServe.create({ contentSafety: ... })` and any host primitive that dispatches on extension or mime; `b.guardAll.list()` returns the registered roster (operator audit aid); `b.guardAll.GUARDS` / `SHARED_PROFILES` / `SHARED_POSTURES` exposed as readonly registry exports."
3566
+ },
3567
+ {
3568
+ "title": "Opt-out",
3569
+ "body": "`exceptFor: { csv: { reason: \"no CSV emission in this app\" } }` requires a non-empty `reason` string per opted-out guard (throws at gate-creation time, not silently); `override: { csv: { profile: \"email-attachment\" } }` reaches per-guard extension profiles that aren't in the shared vocabulary."
3570
+ },
3571
+ {
3572
+ "title": "Audit emission",
3573
+ "body": "every gate / byExtension / byContentType call with `opts.audit` wired emits `guardAll.gate.created` exactly once at gate-creation time, recording the full `active` + `skipped` roster (each skipped entry carries its `reason`)."
3574
+ },
3575
+ {
3576
+ "title": "Registry contract",
3577
+ "body": "every primitive registered into `b.guardAll` MUST export `NAME` (string), `MIME_TYPES` (frozen array), `EXTENSIONS` (frozen array), `PROFILES` containing the shared vocabulary (`strict` / `balanced` / `permissive`), `COMPLIANCE_POSTURES` containing the shared regulatory shapes (`hipaa` / `pci-dss` / `gdpr` / `soc2-cc7`), and `gate(opts)` returning a `b.gateContract`-shaped gate. The parity check at module load throws `GuardAllError` with a multi-line failure list if any registered guard is missing the contract — this is the framework's mechanism for keeping every future guard slice conformant. Duplicate `NAME` / `MIME_TYPE` / `EXTENSION` across guards is also caught at module load."
3578
+ },
3579
+ {
3580
+ "title": "Adjacent",
3581
+ "body": "`b.guardCsv` now exports `NAME` / `MIME_TYPES` / `EXTENSIONS` for registry compliance. Wiki harness gains `fs` binding for examples that mkdir staging dirs (joins the existing `path` / `os` bindings)."
3582
+ },
3583
+ {
3584
+ "title": "Tests",
3585
+ "body": "56 new layer-0 assertions: surface, registry parity (every member declares the full contract), default-on (no `exceptFor` → every guard active), `exceptFor` reason validation (missing / blank / non-object / unknown name throws), `override` shape validation, profile-vocabulary enforcement (per-guard extensions like csv's `email-attachment` rejected at the aggregator), posture-vocabulary enforcement, audit creation roster, dispatch correctness (text/csv routes to csv guard, unrelated mime bypasses), byExtension / byContentType output shape."
3586
+ }
3587
+ ]
3588
+ }
3589
+ ]
3590
+ },
3591
+ {
3592
+ "version": "0.7.5",
3593
+ "date": "2026-05-04",
3594
+ "headline": "`b.gateContract` foundation + `b.guardCsv` content-safety primitive",
3595
+ "summary": "The guard-* family of content-safety gates begins here. `b.gateContract` provides the uniform composition contract every future guard-* primitive implements, and `b.guardCsv` is the first member — full v1-defensible scope covering formula injection, dangerous functions, bidi/control/zero-width chars, CSV bombs, dialect ambiguity, and schema-bound serialization. Also includes a pre-v1 breaking change to `b.csv.stringify`.",
3596
+ "sections": [
3597
+ {
3598
+ "heading": "Added",
3599
+ "items": [
3600
+ {
3601
+ "title": "`b.gateContract` — uniform composition contract for content-safety gates",
3602
+ "body": "Provides `defineGate` / `runGate` / `composeGates` / `multiplexGates` / `contentTypeMux` / `shadowMode` / `canaryGate` / `cachingGate` / `workerThreadGate` / `buildProfile` / `composeHooks` / `summarizeIssues` / `validateGateShape`, plus mode posture (enforce / warn-only / shadow / audit-only / log-only / canary), hook system (`beforeCheck` / `afterCheck` / `onIssue` / `onSanitize` / `onRefuse` / `onAudit`), forensic snapshot store integration, decision cache, and runtime cap via `safeAsync.withTimeout`. Every future guard-* primitive composes this contract."
3603
+ },
3604
+ {
3605
+ "title": "`b.guardCsv` — CSV content-safety gate (full v1 scope)",
3606
+ "body": "Surface: `serialize` / `validate` / `sanitize` / `escapeCell` / `detect` / `parse` / `stringify` / `schema` / `gate` / `buildProfile` / `compliancePosture` / `loadRulePack`. Threat catalogue covers formula injection (5 modes: prefix-tab / prefix-quote / wrap-with-quotes-and-prefix / reject / allowlist) with all 8 ASCII triggers (`= + - @ TAB CR LF |`) plus full-width variants (U+FF1D / U+FF0B / U+FF0D / U+FF20) per OWASP locale catalog; dangerous-function denylist (WEBSERVICE / HYPERLINK / IMAGE / IMPORT* / RTD / DDE / CALL / GOOGLEFINANCE / GOOGLETRANSLATE) surfaced as critical regardless of broader formula policy; Unicode bidi override (CVE-2021-42574 Trojan Source); homoglyph mixed-script detection; C0 control chars; null bytes; BOM mid-stream injection; zero-width chars; dialect ambiguity; CSV bombs (per-cell / total / sanitize-amplification caps); numeric precision loss above `Number.MAX_SAFE_INTEGER`; trailing-whitespace exfiltration policy; PII redaction (composes `b.redact`); and schema-bound serializer with type / regex / range / nullable validation. Profiles: `strict` (OWASP-recommended `prefix-tab` default — Excel-resistant) / `balanced` / `permissive` / `email-attachment`. Compliance postures: `hipaa` / `pci-dss` / `gdpr` / `soc2-cc7`."
3607
+ },
3608
+ {
3609
+ "title": "Codepoint-table programmatic regex composition",
3610
+ "body": "Threat-detection regex literals are composed programmatically from numeric codepoint range tables (`BIDI_RANGES` / `C0_CTRL_RANGES` / `ZERO_WIDTH_RANGES` / `HOMOGLYPH_RANGES` / `FORMULA_PREFIX_CPS`). `_charClass(ranges)` renders into a regex character class via `\\uXXXX` escapes at module load. The source file never embeds the attack characters themselves, only their codepoint numbers; the detector composes the way an attacker would compose the payload."
3611
+ },
3612
+ {
3613
+ "title": "Host primitive integration — `b.staticServe` and `b.fileUpload`",
3614
+ "body": "`b.staticServe` gains `contentSafety: { \".csv\": gate, ... }` extension-keyed gate map that runs after the MIME allowlist and before headers (sanitize replaces body buffer; refuse returns 415). `b.fileUpload` gains `contentSafety: gate` running before `onFinalize` (refuse throws `CONTENT_SAFETY_REFUSED`; sanitize replaces `bodyBuffer`)."
3615
+ },
3616
+ {
3617
+ "title": "`validateOpts.optionalPlainObject(value, label, ErrorClass, code, description)`",
3618
+ "body": "Consolidates the `if (typeof !== \"object\" || Array.isArray) throw` cascade across api-key / db-declare-view / static / file-upload. Registered in `KNOWN_ANTIPATTERNS` so future re-implementations fail the n=1 gate."
3619
+ }
3620
+ ]
3621
+ },
3622
+ {
3623
+ "heading": "Removed",
3624
+ "items": [
3625
+ {
3626
+ "title": "`b.csv.stringify` formula-injection opts (pre-v1 breaking)",
3627
+ "body": "`b.csv.stringify` no longer accepts `preventFormulaInjection` / `formulaPrefixChars` opts. The partial single-mode defense it offered (only `=`/`+`/`-`/`@`/TAB/CR ASCII prefixes, missing LF / `|` / full-width formula chars) was a false-confidence path. `b.csv` is now documented as trusted-source-only emission (RFC 4180 quoting + anti-DoS bounds); any user-supplied cells route through `b.guardCsv.serialize` / `.gate` for the full threat catalogue. The `b.guardCsv.parse` / `.stringify` re-exports are also removed — operators use `b.csv.parse` / `.stringify` directly for pure parsing."
3628
+ }
3629
+ ]
3630
+ }
3631
+ ],
3632
+ "references": [
3633
+ {
3634
+ "label": "OWASP CSV Injection",
3635
+ "url": "https://owasp.org/www-community/attacks/CSV_Injection"
3636
+ },
3637
+ {
3638
+ "label": "RFC 4180 CSV",
3639
+ "url": "https://www.rfc-editor.org/rfc/rfc4180"
3640
+ },
3641
+ {
3642
+ "label": "CVE-2021-42574 Trojan Source",
3643
+ "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-42574"
3644
+ }
3645
+ ]
3646
+ },
3647
+ {
3648
+ "version": "0.7.4",
3649
+ "date": "2026-05-04",
3650
+ "headline": "CI gate alignment + wiki cross-platform fix + redis-client opts forwarding consolidation",
3651
+ "summary": "Two CI-only gates added to the release workflow (`scripts/check-api-snapshot.js` public-API drift detection and Linux-container wiki e2e cross-platform example execution), `api-snapshot.json` baseline refreshed, and a new `redisClient.pickClientOpts` helper consolidates ~40 lines of duplicated forwarding across the four Redis-backed primitives.",
3652
+ "sections": [
3653
+ {
3654
+ "heading": "Added",
3655
+ "items": [
3656
+ {
3657
+ "title": "`redisClient.pickClientOpts(cfg, prefix?)`",
3658
+ "body": "New helper returns the standard 9-key opts bag (`url` / `password` / `username` / `tls` / `ca` / `servername` / `connectTimeoutMs` / `commandTimeoutMs` / `maxReconnectAttempts`). The `cache-redis`, `pubsub-redis`, `queue-redis`, and `cache` redis-backend creators all migrated to it, eliminating duplicated inline forwarding across the four files."
3659
+ },
3660
+ {
3661
+ "title": "`scripts/check-api-snapshot.js` gate + Linux-container wiki e2e",
3662
+ "body": "Public-API drift detection now runs in the local release workflow, and the wiki e2e additionally runs inside a Linux container so cross-platform example execution is caught before push. The `api-snapshot.json` baseline was refreshed for the first time since v0.6.17 (40+ patches of accumulated drift; `b.objectStoreRetry` removal in v0.7.0 was the breaking entry)."
3663
+ }
3664
+ ]
3665
+ },
3666
+ {
3667
+ "heading": "Fixed",
3668
+ "items": [
3669
+ {
3670
+ "title": "Wiki file-upload example cross-platform path",
3671
+ "body": "The example previously hardcoded `stagingDir: \"/var/lib/myapp/uploads\"` which mkdir-passed on Windows but failed `EACCES` on Linux/macOS CI runners. Replaced with `path.join(os.tmpdir(), \"myapp-uploads-\" + Date.now())` and a self-contained `b.fileUpload.create` + `uploads.close()` round-trip; the Usage block likewise instantiates a real `b.router.create()` + uploads pair. `examples/wiki/test/run-example.js` gains `path` + `os` bindings so wiki examples can write idiomatic `path.join(os.tmpdir(), ...)` without falling foul of the harness's `var X = require(...)` line stripper."
3672
+ }
3673
+ ]
3674
+ },
3675
+ {
3676
+ "heading": "Detectors",
3677
+ "items": [
3678
+ {
3679
+ "title": "`inline-redis-client-opts-forwarding`",
3680
+ "body": "New `KNOWN_ANTIPATTERNS` entry catches future re-implementations of the redis-client opts forwarding bag at n=1, so the consolidation can't regress silently."
3681
+ },
3682
+ {
3683
+ "title": "`testNoDuplicateCodeBlocks` / `testNoUnresolvedMarkers` / `testNoTierTerminologyInLib` tuning",
3684
+ "body": "`MIN_DISTINCT_FILES` lowered 3 → 2 and `MIN_DISTINCT_TOKENS` lowered 6 → 5 for more aggressive duplicate detection. The forbidden-marker scan adds `defer`, and the forbidden-internal-terminology scan adds the three numeric-suffix forms. `lib/log-stream-syslog.js`'s socket error-handler comment was reworded from \"defer to 'close' for reconnect\" to \"reconnect handled in the close listener\" — same behaviour, no marker false-positive."
3685
+ }
3686
+ ]
3687
+ }
3688
+ ]
3689
+ },
3690
+ {
3691
+ "version": "0.7.3",
3692
+ "date": "2026-05-04",
3693
+ "headline": "`b.middleware.apiEncrypt` per-session keying mode (opt-in). Per-request keying stays the default —",
3694
+ "summary": "every existing test passes unchanged. Operators opt in by passing `keying: \"per-session\"`; the first request in a session carries the bootstrap envelope `{ _ek, _ct, _ts, _nonce, _sid, _ctr }` and the server stores the session key keyed by `_sid` (UUID v4); subsequent requests in.",
3695
+ "sections": [
3696
+ {
3697
+ "heading": "Added",
3698
+ "items": [
3699
+ {
3700
+ "title": "Tests",
3701
+ "body": "13 new layer-0 assertions covering default-keying invariant, opt validation (bad keying value / TTL / store shape), bootstrap-and-reuse round-trip (KEM amortization confirmed by inspecting subsequent envelope shape), unknown-sid 401, counter-replay 400, TTL-expiry 401, max-responses-rotation 401, response counter monotonic check, sessionInfo / resetSession client API, observability emission."
3702
+ }
3703
+ ]
3704
+ }
3705
+ ]
3706
+ },
3707
+ {
3708
+ "version": "0.7.2",
3709
+ "date": "2026-05-04",
3710
+ "headline": "Symmetric upload + download primitives ship with full v1-defensible feature sets",
3711
+ "summary": "`b.fileUpload` chunked upload primitive: operators wire HTTP routes for per-chunk PUT (`fileUpload.acceptChunk`) and finalize POST (`fileUpload.finalize`); the framework owns the chunk lifecycle (per-chunk SHA3-512, atomic chunk write to `<stagingDir>/<uploadId>/<index>`, rea.",
3712
+ "sections": [
3713
+ {
3714
+ "heading": "Added",
3715
+ "items": [
3716
+ {
3717
+ "title": "`b.fileUpload`",
3718
+ "body": "chunked upload primitive: operators wire HTTP routes for per-chunk PUT (`fileUpload.acceptChunk`) and finalize POST (`fileUpload.finalize`); the framework owns the chunk lifecycle (per-chunk SHA3-512, atomic chunk write to `<stagingDir>/<uploadId>/<index>`, reassemble in manifest order, verify total SHA3-512) and hands the assembled buffer to an operator-supplied `onFinalize` callback. Surface includes `init` / `acceptChunk` / `finalize` / `status` / `list` / `cancelUpload` / `purgeIncomplete`. v1 features: per-actor active-upload quota, total staging-bytes quota, MIME magic-byte allowlist (composes `b.fileType`), `onChunk` operator hook, idle-upload timeout, stream reassembly above `maxStreamReassemblyBytes` (sequential async iterator over chunk files instead of `Buffer.concat`), permissions integration, metadata stash with cap, path-safe upload-ID regex, mode 0o700 staging dir, idempotent re-PUT, force-cancel, audit emission with 5-W actor context, observability counters."
3719
+ },
3720
+ {
3721
+ "title": "`b.staticServe`",
3722
+ "body": "download primitive expanded with the symmetric feature set: permissions gate (403), compliance retention gate (451), force-revoke instance method + revoke-store opt (404), MIME magic-byte allowlist (415), RFC 7233 single-range support (multi-range refused with 416), full conditional-request set (`If-None-Match` / `If-Match` / `If-Modified-Since` / `If-Unmodified-Since`), per-actor + global bandwidth quotas via `b.cache` cluster-shared token-buckets (429 + Retry-After), per-actor concurrency cap (429), `onServe` per-request operator hook, `maxIdleMs` stalled-stream timeout, cancellation propagation (client disconnect destroys file stream + releases concurrency slot), audit + observability emission with 5-W actor context. ETag is now SHA3-512-truncated for PQC posture; the SRI integrity helper keeps SHA-384 because the W3C subresource-integrity spec only allows sha256/sha384/sha512. Object-store backends (sigv4 / gcs / azure-blob) gain a new `getResponse(key, opts?)` method that forwards `range` / `ifNoneMatch` / `ifMatch` / `ifModifiedSince` / `ifUnmodifiedSince` opts to the backend's protocol-specific headers and returns `{ statusCode, body, etag, lastModified, contentRange, size, contentType }` — operators wiring object-store-backed download routes route conditional + range from the client request straight through. Existing `get(key)` returns just the body for back-compat."
3723
+ },
3724
+ {
3725
+ "title": "`HTTP_STATUS`",
3726
+ "body": "constants extended with `PARTIAL_CONTENT` / `RANGE_NOT_SATISFIABLE` / `PRECONDITION_FAILED` / `UNAVAILABLE_FOR_LEGAL_REASONS`."
3727
+ },
3728
+ {
3729
+ "title": "New helpers",
3730
+ "body": "`validateOpts.optionalNonEmptyStringArray(value, label, ErrorClass, code?)` and `validateOpts.optionalObjectWithMethod(value, method, label, ErrorClass, code?, description?)` consolidate the recurring \"string-array\" and \"duck-typed handle\" inline cascades across api-key / file-upload / seeders / notify / webhook / db-role-for. Both registered in `KNOWN_ANTIPATTERNS` so future re-implementations fail the n=1 gate."
3731
+ },
3732
+ {
3733
+ "title": "Tests",
3734
+ "body": "28 new layer-0 fileUpload assertions (init / acceptChunk / finalize / status / list / cancel / quotas / MIME allowlist / onChunk / idle / stream reassembly / permissions) plus 24 new staticServe assertions (range / suffix-range / open-end-range / unsatisfiable-range / multi-range refused / acceptRanges off / If-Match / If-Modified-Since / If-Unmodified-Since / permissions / retention / revoke / MIME allowlist / onServe / audit / stats / invalidateMeta / quotas / concurrency cap). Wiki page `examples/wiki/seeders/prod/pages/file-upload.js` added; routing.js section for `b.staticServe` rewritten to document the v1 surface. README \"what ships in the box\" Communication + Routing bullets updated."
3735
+ }
3736
+ ]
3737
+ }
3738
+ ],
3739
+ "references": [
3740
+ {
3741
+ "label": "RFC 7233",
3742
+ "url": "https://www.rfc-editor.org/rfc/rfc7233.html"
3743
+ }
3744
+ ]
3745
+ },
3746
+ {
3747
+ "version": "0.7.1",
3748
+ "date": "2026-05-04",
3749
+ "headline": "`b.websocket` route opts gain `handshakeGuid` to override the RFC 6455 §1.3 magic string used in",
3750
+ "summary": "the `Sec-WebSocket-Accept` derivation. Default stays `258EAFA5-E914-47DA-95CA-C5AB0DC85B11`.",
3751
+ "sections": [
3752
+ {
3753
+ "heading": "Added",
3754
+ "items": [
3755
+ {
3756
+ "title": "Tests",
3757
+ "body": "3 new layer-0 assertions in `test/00-primitives.js` (custom GUID produces different accept key, empty / null falls back to RFC default, malformed handshakeGuid rejected at config time)."
3758
+ }
3759
+ ]
3760
+ }
3761
+ ],
3762
+ "references": [
3763
+ {
3764
+ "label": "RFC 6455",
3765
+ "url": "https://www.rfc-editor.org/rfc/rfc6455.html"
3766
+ }
3767
+ ]
3768
+ },
3769
+ {
3770
+ "version": "0.7.0",
3771
+ "date": "2026-05-04",
3772
+ "headline": "Codebase-patterns hardening sweep + primitive consolidation",
3773
+ "summary": "The duplicate-block detector in `test/layer-0-primitives/codebase-patterns.test.js` ran at MIN_DISTINCT_FILES=3 and surfaced ~50 inline-shape clusters that had proliferated across lib/ — the kind of soft drift that's invisible at higher thresholds and hides re-introduction of bug classes the framework already swept once.",
3774
+ "sections": [
3775
+ {
3776
+ "heading": "Fixed",
3777
+ "items": [
3778
+ {
3779
+ "title": "Catalog gate",
3780
+ "body": "`KNOWN_ANTIPATTERNS` in the codebase-patterns test now has 24 entries firing at n=1, so future code re-introducing any of the registered inline shapes (even one new file) fails the gate immediately. Pre-v0.7.0 the duplicate-block detector required n>=3 to fire; the catalog closes the gap by registering each extracted primitive's inline shape so it can't drift back in."
3781
+ },
3782
+ {
3783
+ "title": "Cluster allowlist",
3784
+ "body": "`KNOWN_CLUSTERS` allowlist (n>=3 detector) has 25 entries with documented structural reasons (parser error class signatures don't fit the framework's `(code, message)` contract; framework-convention shapes like middleware factories; future consolidation candidates)."
3785
+ },
3786
+ {
3787
+ "title": "Cleanup",
3788
+ "body": "deleted dead re-export shims `lib/object-store/retry.js` (re-exported `lib/retry.js`) and `lib/auth/totp.js` (re-exported `lib/totp.js`); both fit the rule \"pre-v1 frameworks have no operators to compatibly upgrade — every legacy fallback is dead code.\" Renamed `lib/internal-sha1-hibp.js` → `lib/framework-sha1-hibp.js` to match the lib naming convention (the `internal-` prefix wasn't in the convention's five-bucket list; `framework-` is the canonical \"restricted-use\" bucket alongside `framework-error.js` / `framework-schema.js`)."
3789
+ },
3790
+ {
3791
+ "title": "No operator-facing API breakage",
3792
+ "body": "`b.objectStoreRetry` was removed from the public surface (operators use `b.retry` directly, which has been the canonical primitive since v0.2.24)."
3793
+ },
3794
+ {
3795
+ "title": "Eslint config tightened",
3796
+ "body": "added `eqeqeq` (with the `null` exception for the `== null` null-or-undefined idiom), `no-throw-literal`, `no-promise-executor-return`, `default-case`, `no-loss-of-precision`. The previous config was hiding 11 real errors: 8 sites of `return resolve()` / `return done()` inside `new Promise(executor)` (return value silently discarded) across app / dev / http-client / mail / router / wiki/integration / 00-primitives, plus a missing default-case + 2 intentional template-language `==` / `!=` operators in template.js's binary-op evaluator (now allowed via inline `// eslint-disable-next-line eqeqeq -- template language operator`). Also adds `structuredClone` to the Node-globals list so db.js's deep-clone calls don't trip `no-undef`."
3797
+ },
3798
+ {
3799
+ "title": "Release-workflow gate added",
3800
+ "body": "Linux container smoke `docker run --rm -v \"/$(pwd):/blamejs\" -w //blamejs node:24-alpine node test/smoke.js` is now part of the release flow. Catches lingering-handle bugs that pass on Windows / macOS but hang or error on Linux CI."
3801
+ },
3802
+ {
3803
+ "title": "Tests",
3804
+ "body": "smoke 7338 / Linux container smoke 7338 (148s) / wiki e2e 178 / per-primitive integration 16 files / wiki integration green / eslint clean / shellcheck clean."
3805
+ }
3806
+ ]
3807
+ }
3808
+ ]
3809
+ }
3810
+ ]
3811
+ }