rcf-lite 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (625) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +2 -0
  3. package/bin/view-supervisor-child.mjs +0 -0
  4. package/blueprints/application-api-rest/README.md +40 -0
  5. package/blueprints/application-api-rest/assets/middleware/auth-classes.md +45 -0
  6. package/blueprints/application-api-rest/assets/openapi/openapi-skeleton.yaml +322 -0
  7. package/blueprints/application-api-rest/assets/sample-data/sample-resources.json +53 -0
  8. package/blueprints/application-api-rest/assets/samples/request-response-pairs.md +204 -0
  9. package/blueprints/application-api-rest/blueprint.json +265 -0
  10. package/blueprints/application-api-rest/contributions/adrs/adr-301-application-api-rest-error-envelope.json +25 -0
  11. package/blueprints/application-api-rest/contributions/adrs/adr-302-application-api-rest-auth-model.json +25 -0
  12. package/blueprints/application-api-rest/contributions/adrs/adr-303-application-api-rest-api-versioning.json +25 -0
  13. package/blueprints/application-api-rest/contributions/adrs/adr-304-application-api-rest-logging.json +25 -0
  14. package/blueprints/application-api-rest/contributions/adrs/adr-305-application-api-rest-pagination.json +25 -0
  15. package/blueprints/application-api-rest/contributions/adrs/adr-306-application-api-rest-idempotency.json +25 -0
  16. package/blueprints/application-api-rest/contributions/adrs/adr-307-application-api-rest-openapi-generation.json +25 -0
  17. package/blueprints/application-api-rest/contributions/adrs/adr-308-application-api-rest-rate-limiting.json +25 -0
  18. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-001.json +18 -0
  19. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-002.json +18 -0
  20. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-003.json +18 -0
  21. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-004.json +18 -0
  22. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-005.json +18 -0
  23. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-006.json +18 -0
  24. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-007.json +18 -0
  25. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-008.json +18 -0
  26. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-009.json +18 -0
  27. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-010.json +18 -0
  28. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-011.json +18 -0
  29. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-012.json +18 -0
  30. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-013.json +18 -0
  31. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-014.json +18 -0
  32. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-015.json +18 -0
  33. package/blueprints/application-api-rest/contributions/requirements/application-api-rest-req-016.json +18 -0
  34. package/blueprints/application-api-rest/contributions/tacs/tac-301-application-api-rest-request-pipeline.json +48 -0
  35. package/blueprints/application-api-rest/contributions/tacs/tac-302-application-api-rest-auth-middleware.json +41 -0
  36. package/blueprints/application-api-rest/contributions/tacs/tac-303-application-api-rest-contract-surface.json +46 -0
  37. package/blueprints/application-api-rest/contributions/tacs/tac-304-application-api-rest-resource-layer.json +46 -0
  38. package/blueprints/application-api-rest/contributions/tacs/tac-305-application-api-rest-observability.json +40 -0
  39. package/blueprints/application-api-rest/contributions/tacs/tac-306-application-api-rest-operability.json +46 -0
  40. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2101.json +72 -0
  41. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2102.json +73 -0
  42. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2103.json +63 -0
  43. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2104.json +72 -0
  44. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2105.json +63 -0
  45. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2106.json +73 -0
  46. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2107.json +90 -0
  47. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2108.json +90 -0
  48. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2109.json +72 -0
  49. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2110.json +72 -0
  50. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2111.json +72 -0
  51. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2112.json +72 -0
  52. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2113.json +73 -0
  53. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2114.json +63 -0
  54. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2115.json +63 -0
  55. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2116.json +63 -0
  56. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2117.json +64 -0
  57. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2118.json +72 -0
  58. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2119.json +63 -0
  59. package/blueprints/application-api-rest/contributions/user-stories/application-api-rest-us-2120.json +73 -0
  60. package/blueprints/application-api-rest/docs/topics.md +46 -0
  61. package/blueprints/application-api-rest/guide/application-api-rest.md +41 -0
  62. package/blueprints/application-spa/README.md +90 -0
  63. package/blueprints/application-spa/assets/component-specs/actions-and-text-inputs.md +36 -0
  64. package/blueprints/application-spa/assets/component-specs/data-display.md +19 -0
  65. package/blueprints/application-spa/assets/component-specs/feedback-and-status.md +27 -0
  66. package/blueprints/application-spa/assets/component-specs/overlays.md +23 -0
  67. package/blueprints/application-spa/assets/component-specs/selection-controls.md +23 -0
  68. package/blueprints/application-spa/assets/sample-data/sample-entities.json +41 -0
  69. package/blueprints/application-spa/assets/tokens/design-tokens.json +140 -0
  70. package/blueprints/application-spa/assets/tokens/theme.css +169 -0
  71. package/blueprints/application-spa/assets/viewports.md +18 -0
  72. package/blueprints/application-spa/assets/wireframes/dashboard.md +48 -0
  73. package/blueprints/application-spa/assets/wireframes/detail.md +40 -0
  74. package/blueprints/application-spa/assets/wireframes/empty.md +31 -0
  75. package/blueprints/application-spa/assets/wireframes/error.md +46 -0
  76. package/blueprints/application-spa/assets/wireframes/form.md +50 -0
  77. package/blueprints/application-spa/assets/wireframes/list.md +45 -0
  78. package/blueprints/application-spa/assets/wireframes/session-expired.md +35 -0
  79. package/blueprints/application-spa/assets/wireframes/sign-in.md +41 -0
  80. package/blueprints/application-spa/assets/wireframes/sign-out.md +32 -0
  81. package/blueprints/application-spa/blueprint.json +387 -0
  82. package/blueprints/application-spa/contributions/adrs/adr-201-application-spa-routing.json +25 -0
  83. package/blueprints/application-spa/contributions/adrs/adr-202-application-spa-theming.json +25 -0
  84. package/blueprints/application-spa/contributions/adrs/adr-203-application-spa-client-state.json +25 -0
  85. package/blueprints/application-spa/contributions/adrs/adr-204-application-spa-error-envelope.json +25 -0
  86. package/blueprints/application-spa/contributions/adrs/adr-205-application-spa-auth-model.json +25 -0
  87. package/blueprints/application-spa/contributions/adrs/adr-206-application-spa-iconography.json +13 -0
  88. package/blueprints/application-spa/contributions/adrs/adr-207-application-spa-motion.json +13 -0
  89. package/blueprints/application-spa/contributions/adrs/adr-208-application-spa-i18n.json +13 -0
  90. package/blueprints/application-spa/contributions/adrs/adr-209-application-spa-telemetry-naming.json +13 -0
  91. package/blueprints/application-spa/contributions/requirements/application-spa-req-001.json +18 -0
  92. package/blueprints/application-spa/contributions/requirements/application-spa-req-002.json +18 -0
  93. package/blueprints/application-spa/contributions/requirements/application-spa-req-003.json +18 -0
  94. package/blueprints/application-spa/contributions/requirements/application-spa-req-004.json +18 -0
  95. package/blueprints/application-spa/contributions/requirements/application-spa-req-005.json +18 -0
  96. package/blueprints/application-spa/contributions/requirements/application-spa-req-006.json +18 -0
  97. package/blueprints/application-spa/contributions/requirements/application-spa-req-007.json +18 -0
  98. package/blueprints/application-spa/contributions/requirements/application-spa-req-008.json +18 -0
  99. package/blueprints/application-spa/contributions/requirements/application-spa-req-009.json +18 -0
  100. package/blueprints/application-spa/contributions/requirements/application-spa-req-010.json +18 -0
  101. package/blueprints/application-spa/contributions/requirements/application-spa-req-011.json +18 -0
  102. package/blueprints/application-spa/contributions/requirements/application-spa-req-012.json +18 -0
  103. package/blueprints/application-spa/contributions/requirements/application-spa-req-013.json +18 -0
  104. package/blueprints/application-spa/contributions/requirements/application-spa-req-014.json +18 -0
  105. package/blueprints/application-spa/contributions/requirements/application-spa-req-015.json +18 -0
  106. package/blueprints/application-spa/contributions/requirements/application-spa-req-016.json +18 -0
  107. package/blueprints/application-spa/contributions/requirements/application-spa-req-017.json +18 -0
  108. package/blueprints/application-spa/contributions/requirements/application-spa-req-018.json +18 -0
  109. package/blueprints/application-spa/contributions/requirements/application-spa-req-019.json +18 -0
  110. package/blueprints/application-spa/contributions/requirements/application-spa-req-020.json +18 -0
  111. package/blueprints/application-spa/contributions/requirements/application-spa-req-021.json +18 -0
  112. package/blueprints/application-spa/contributions/tacs/tac-201-application-spa-app-shell.json +45 -0
  113. package/blueprints/application-spa/contributions/tacs/tac-202-application-spa-theme-system.json +32 -0
  114. package/blueprints/application-spa/contributions/tacs/tac-203-application-spa-component-library.json +32 -0
  115. package/blueprints/application-spa/contributions/tacs/tac-204-application-spa-client-data-layer.json +27 -0
  116. package/blueprints/application-spa/contributions/tacs/tac-205-application-spa-forms-engine.json +27 -0
  117. package/blueprints/application-spa/contributions/tacs/tac-206-application-spa-telemetry.json +19 -0
  118. package/blueprints/application-spa/contributions/tacs/tac-207-application-spa-token-adherence-probe.json +57 -0
  119. package/blueprints/application-spa/contributions/tacs/tac-208-application-spa-icon-adherence-probe.json +56 -0
  120. package/blueprints/application-spa/contributions/tacs/tac-209-application-spa-csp-styled-adherence-probe.json +61 -0
  121. package/blueprints/application-spa/contributions/tacs/tac-210-application-spa-external-dependency-provisioning-probe.json +70 -0
  122. package/blueprints/application-spa/contributions/tacs/tac-211-application-spa-core-flow-e2e-probe.json +70 -0
  123. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1101.json +72 -0
  124. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1102.json +63 -0
  125. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1103.json +72 -0
  126. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1104.json +63 -0
  127. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1105.json +69 -0
  128. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1106.json +63 -0
  129. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1107.json +63 -0
  130. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1108.json +90 -0
  131. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1109.json +90 -0
  132. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1110.json +100 -0
  133. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1111.json +90 -0
  134. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1112.json +81 -0
  135. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1113.json +82 -0
  136. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1114.json +60 -0
  137. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1115.json +73 -0
  138. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1116.json +72 -0
  139. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1117.json +73 -0
  140. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1118.json +54 -0
  141. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1119.json +63 -0
  142. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1120.json +72 -0
  143. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1121.json +73 -0
  144. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1122.json +69 -0
  145. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1123.json +63 -0
  146. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1124.json +60 -0
  147. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1125.json +69 -0
  148. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1126.json +78 -0
  149. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1127.json +63 -0
  150. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1128.json +51 -0
  151. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1129.json +54 -0
  152. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1130.json +54 -0
  153. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1131.json +72 -0
  154. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1132.json +72 -0
  155. package/blueprints/application-spa/contributions/user-stories/application-spa-us-1133.json +72 -0
  156. package/blueprints/application-spa/docs/topics.md +57 -0
  157. package/blueprints/application-spa/guide/application-spa.md +40 -0
  158. package/blueprints/ci-pipeline/README.md +49 -0
  159. package/blueprints/ci-pipeline/assets/ci-provider-examples/github-actions.yml +61 -0
  160. package/blueprints/ci-pipeline/assets/ci-provider-examples/notes.md +50 -0
  161. package/blueprints/ci-pipeline/assets/report-samples/per-gate.json +12 -0
  162. package/blueprints/ci-pipeline/assets/report-samples/pipeline.json +28 -0
  163. package/blueprints/ci-pipeline/blueprint.json +46 -0
  164. package/blueprints/ci-pipeline/contributions/adrs/adr-701-ci-pipeline-ci-gates.json +25 -0
  165. package/blueprints/ci-pipeline/contributions/adrs/adr-702-ci-pipeline-strict-coverage-gate.json +25 -0
  166. package/blueprints/ci-pipeline/contributions/adrs/adr-703-ci-pipeline-node-only-runner.json +25 -0
  167. package/blueprints/ci-pipeline/contributions/adrs/adr-704-ci-pipeline-report-shape.json +25 -0
  168. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-001.json +18 -0
  169. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-002.json +18 -0
  170. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-003.json +18 -0
  171. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-004.json +18 -0
  172. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-005.json +18 -0
  173. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-006.json +18 -0
  174. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-007.json +18 -0
  175. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-008.json +18 -0
  176. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-009.json +18 -0
  177. package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-010.json +18 -0
  178. package/blueprints/ci-pipeline/contributions/tacs/tac-701-ci-pipeline-gate-runner.json +59 -0
  179. package/blueprints/ci-pipeline/contributions/tacs/tac-702-ci-pipeline-gate-report.json +32 -0
  180. package/blueprints/ci-pipeline/contributions/tacs/tac-703-ci-pipeline-aggregate-report.json +41 -0
  181. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6101.json +37 -0
  182. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6102.json +36 -0
  183. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6103.json +37 -0
  184. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6104.json +37 -0
  185. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6105.json +36 -0
  186. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6106.json +36 -0
  187. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6107.json +37 -0
  188. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6108.json +37 -0
  189. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6109.json +36 -0
  190. package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6110.json +36 -0
  191. package/blueprints/ci-pipeline/docs/topics.md +49 -0
  192. package/blueprints/ci-pipeline/guide/ci-pipeline.md +79 -0
  193. package/blueprints/deploy-cloudflare-workers/README.md +63 -0
  194. package/blueprints/deploy-cloudflare-workers/assets/ci-workflows/build-and-upload.yml.md +109 -0
  195. package/blueprints/deploy-cloudflare-workers/assets/ci-workflows/promote.yml.md +120 -0
  196. package/blueprints/deploy-cloudflare-workers/assets/verification/served-surface-probes.md +90 -0
  197. package/blueprints/deploy-cloudflare-workers/assets/wrangler-samples/bootstrap-vs-steady-state.md +87 -0
  198. package/blueprints/deploy-cloudflare-workers/assets/wrangler-samples/preview-alias-shape.md +86 -0
  199. package/blueprints/deploy-cloudflare-workers/assets/wrangler-samples/wrangler-toml-shape.md +85 -0
  200. package/blueprints/deploy-cloudflare-workers/blueprint.json +48 -0
  201. package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1301-deploy-cloudflare-workers-deployment-target.json +30 -0
  202. package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1302-deploy-cloudflare-workers-default-vendor.json +35 -0
  203. package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1303-deploy-cloudflare-workers-preview-vs-production.json +30 -0
  204. package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1304-deploy-cloudflare-workers-rollback-is-promote.json +25 -0
  205. package/blueprints/deploy-cloudflare-workers/contributions/adrs/adr-1305-deploy-cloudflare-workers-dev-mode-drift.json +25 -0
  206. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-001.json +18 -0
  207. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-002.json +18 -0
  208. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-003.json +18 -0
  209. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-004.json +18 -0
  210. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-005.json +18 -0
  211. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-006.json +18 -0
  212. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-007.json +18 -0
  213. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-008.json +18 -0
  214. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-009.json +18 -0
  215. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-010.json +18 -0
  216. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-011.json +18 -0
  217. package/blueprints/deploy-cloudflare-workers/contributions/requirements/deploy-cloudflare-workers-req-012.json +18 -0
  218. package/blueprints/deploy-cloudflare-workers/contributions/tacs/tac-1301-deploy-cloudflare-workers-deploy-adapter.json +54 -0
  219. package/blueprints/deploy-cloudflare-workers/contributions/tacs/tac-1302-deploy-cloudflare-workers-wrangler-manifest.json +45 -0
  220. package/blueprints/deploy-cloudflare-workers/contributions/tacs/tac-1303-deploy-cloudflare-workers-preview-url-resolver.json +46 -0
  221. package/blueprints/deploy-cloudflare-workers/contributions/tacs/tac-1304-deploy-cloudflare-workers-promote-gate.json +57 -0
  222. package/blueprints/deploy-cloudflare-workers/contributions/tacs/tac-1305-deploy-cloudflare-workers-served-surface-verifier.json +51 -0
  223. package/blueprints/deploy-cloudflare-workers/contributions/tacs/tac-1306-deploy-cloudflare-workers-deploy-ci-workflows.json +64 -0
  224. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12101.json +54 -0
  225. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12102.json +45 -0
  226. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12103.json +46 -0
  227. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12104.json +46 -0
  228. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12105.json +46 -0
  229. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12106.json +46 -0
  230. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12107.json +46 -0
  231. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12108.json +46 -0
  232. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12109.json +47 -0
  233. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12110.json +46 -0
  234. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12111.json +45 -0
  235. package/blueprints/deploy-cloudflare-workers/contributions/user-stories/deploy-cloudflare-workers-us-12112.json +46 -0
  236. package/blueprints/deploy-cloudflare-workers/docs/topics.md +46 -0
  237. package/blueprints/deploy-cloudflare-workers/guide/deploy-cloudflare-workers.md +74 -0
  238. package/blueprints/email-smtp-resend/README.md +45 -0
  239. package/blueprints/email-smtp-resend/assets/from-address-rotation-pattern.md +44 -0
  240. package/blueprints/email-smtp-resend/assets/resend-verification-pointer.md +25 -0
  241. package/blueprints/email-smtp-resend/assets/stub-adapter.md +65 -0
  242. package/blueprints/email-smtp-resend/blueprint.json +23 -0
  243. package/blueprints/email-smtp-resend/contributions/adrs/adr-401-email-smtp-resend-transport-choice.json +30 -0
  244. package/blueprints/email-smtp-resend/contributions/adrs/adr-402-email-smtp-resend-retry-and-backoff-posture.json +30 -0
  245. package/blueprints/email-smtp-resend/contributions/requirements/email-smtp-resend-req-001.json +18 -0
  246. package/blueprints/email-smtp-resend/contributions/requirements/email-smtp-resend-req-002.json +18 -0
  247. package/blueprints/email-smtp-resend/contributions/requirements/email-smtp-resend-req-003.json +18 -0
  248. package/blueprints/email-smtp-resend/contributions/requirements/email-smtp-resend-req-004.json +18 -0
  249. package/blueprints/email-smtp-resend/contributions/requirements/email-smtp-resend-req-005.json +18 -0
  250. package/blueprints/email-smtp-resend/contributions/requirements/email-smtp-resend-req-006.json +18 -0
  251. package/blueprints/email-smtp-resend/contributions/tacs/tac-401-email-smtp-resend-send-adapter.json +53 -0
  252. package/blueprints/email-smtp-resend/contributions/tacs/tac-402-email-smtp-resend-webhook-verifier.json +56 -0
  253. package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4101.json +45 -0
  254. package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4102.json +36 -0
  255. package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4103.json +45 -0
  256. package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4104.json +45 -0
  257. package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4105.json +36 -0
  258. package/blueprints/email-smtp-resend/contributions/user-stories/email-smtp-resend-us-4106.json +37 -0
  259. package/blueprints/email-smtp-resend/docs/topics.md +44 -0
  260. package/blueprints/email-smtp-resend/guide/email-smtp-resend.md +71 -0
  261. package/blueprints/observability-essentials/README.md +53 -0
  262. package/blueprints/observability-essentials/assets/probe-samples/liveness-body.json +4 -0
  263. package/blueprints/observability-essentials/assets/probe-samples/readiness-body.json +18 -0
  264. package/blueprints/observability-essentials/assets/status-page-samples/status-page-clean.html +22 -0
  265. package/blueprints/observability-essentials/assets/status-page-samples/status-page-with-notice.html +25 -0
  266. package/blueprints/observability-essentials/blueprint.json +54 -0
  267. package/blueprints/observability-essentials/contributions/adrs/adr-801-observability-essentials-health-probes.json +30 -0
  268. package/blueprints/observability-essentials/contributions/adrs/adr-802-observability-essentials-readiness-semantics.json +30 -0
  269. package/blueprints/observability-essentials/contributions/adrs/adr-803-observability-essentials-status-page-contract.json +30 -0
  270. package/blueprints/observability-essentials/contributions/adrs/adr-804-observability-essentials-probe-secrecy.json +25 -0
  271. package/blueprints/observability-essentials/contributions/adrs/adr-805-observability-essentials-notification-outcome-model.json +25 -0
  272. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-001.json +18 -0
  273. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-002.json +18 -0
  274. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-003.json +18 -0
  275. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-004.json +18 -0
  276. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-005.json +18 -0
  277. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-006.json +18 -0
  278. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-007.json +18 -0
  279. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-008.json +18 -0
  280. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-009.json +18 -0
  281. package/blueprints/observability-essentials/contributions/requirements/observability-essentials-req-010.json +18 -0
  282. package/blueprints/observability-essentials/contributions/tacs/tac-801-observability-essentials-liveness-probe.json +32 -0
  283. package/blueprints/observability-essentials/contributions/tacs/tac-802-observability-essentials-readiness-probe.json +43 -0
  284. package/blueprints/observability-essentials/contributions/tacs/tac-803-observability-essentials-status-page.json +37 -0
  285. package/blueprints/observability-essentials/contributions/tacs/tac-804-observability-essentials-notification-outcome.json +42 -0
  286. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7101.json +45 -0
  287. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7102.json +45 -0
  288. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7103.json +45 -0
  289. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7104.json +45 -0
  290. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7105.json +45 -0
  291. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7106.json +45 -0
  292. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7107.json +46 -0
  293. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7108.json +47 -0
  294. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7109.json +45 -0
  295. package/blueprints/observability-essentials/contributions/user-stories/observability-essentials-us-7110.json +46 -0
  296. package/blueprints/observability-essentials/docs/topics.md +57 -0
  297. package/blueprints/observability-essentials/guide/observability-essentials.md +68 -0
  298. package/blueprints/observability-probe-endpoints/README.md +53 -0
  299. package/blueprints/observability-probe-endpoints/assets/docker/dockerfile-healthcheck.md +19 -0
  300. package/blueprints/observability-probe-endpoints/assets/kubernetes/podspec-probes-separate-port.md +54 -0
  301. package/blueprints/observability-probe-endpoints/assets/kubernetes/podspec-probes.md +36 -0
  302. package/blueprints/observability-probe-endpoints/assets/load-balancer/health-check-config.md +23 -0
  303. package/blueprints/observability-probe-endpoints/assets/systemd/notify-service.md +30 -0
  304. package/blueprints/observability-probe-endpoints/assets/uptime-monitor/monitor-config.md +22 -0
  305. package/blueprints/observability-probe-endpoints/blueprint.json +44 -0
  306. package/blueprints/observability-probe-endpoints/contributions/adrs/adr-1501-observability-probe-endpoints-health-probes.json +30 -0
  307. package/blueprints/observability-probe-endpoints/contributions/adrs/adr-1502-observability-probe-endpoints-readiness-semantics.json +30 -0
  308. package/blueprints/observability-probe-endpoints/contributions/adrs/adr-1503-observability-probe-endpoints-kubernetes-default.json +30 -0
  309. package/blueprints/observability-probe-endpoints/contributions/adrs/adr-1504-observability-probe-endpoints-separate-port-option.json +30 -0
  310. package/blueprints/observability-probe-endpoints/contributions/adrs/adr-1505-observability-probe-endpoints-external-response-secrecy.json +30 -0
  311. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-001.json +18 -0
  312. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-002.json +18 -0
  313. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-003.json +18 -0
  314. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-004.json +18 -0
  315. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-005.json +18 -0
  316. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-006.json +18 -0
  317. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-007.json +18 -0
  318. package/blueprints/observability-probe-endpoints/contributions/requirements/observability-probe-endpoints-req-008.json +18 -0
  319. package/blueprints/observability-probe-endpoints/contributions/tacs/tac-1501-observability-probe-endpoints-profile-resolver.json +39 -0
  320. package/blueprints/observability-probe-endpoints/contributions/tacs/tac-1502-observability-probe-endpoints-handler-set.json +40 -0
  321. package/blueprints/observability-probe-endpoints/contributions/tacs/tac-1503-observability-probe-endpoints-listener-topology.json +51 -0
  322. package/blueprints/observability-probe-endpoints/contributions/tacs/tac-1504-observability-probe-endpoints-non-http-adapters.json +39 -0
  323. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14101.json +46 -0
  324. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14102.json +46 -0
  325. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14103.json +46 -0
  326. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14104.json +45 -0
  327. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14105.json +46 -0
  328. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14106.json +45 -0
  329. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14107.json +45 -0
  330. package/blueprints/observability-probe-endpoints/contributions/user-stories/observability-probe-endpoints-us-14108.json +46 -0
  331. package/blueprints/observability-probe-endpoints/docs/topics.md +51 -0
  332. package/blueprints/observability-probe-endpoints/guide/observability-probe-endpoints.md +83 -0
  333. package/blueprints/persistence-data-d1/README.md +50 -0
  334. package/blueprints/persistence-data-d1/assets/batch-usage/batch-atomicity-example.md +72 -0
  335. package/blueprints/persistence-data-d1/assets/facade-shape/facade-module-shape.md +115 -0
  336. package/blueprints/persistence-data-d1/assets/migration-shape/migration-file-shape.md +119 -0
  337. package/blueprints/persistence-data-d1/assets/wrangler-config/d1-binding-shape.md +93 -0
  338. package/blueprints/persistence-data-d1/blueprint.json +42 -0
  339. package/blueprints/persistence-data-d1/contributions/adrs/adr-1401-persistence-data-d1-store-model.json +30 -0
  340. package/blueprints/persistence-data-d1/contributions/adrs/adr-1402-persistence-data-d1-migration-discipline.json +30 -0
  341. package/blueprints/persistence-data-d1/contributions/adrs/adr-1403-persistence-data-d1-event-secrecy.json +25 -0
  342. package/blueprints/persistence-data-d1/contributions/adrs/adr-1404-persistence-data-d1-store-boundary.json +25 -0
  343. package/blueprints/persistence-data-d1/contributions/adrs/adr-1405-persistence-data-d1-recovery-model.json +30 -0
  344. package/blueprints/persistence-data-d1/contributions/requirements/persistence-data-d1-req-001.json +18 -0
  345. package/blueprints/persistence-data-d1/contributions/requirements/persistence-data-d1-req-002.json +18 -0
  346. package/blueprints/persistence-data-d1/contributions/requirements/persistence-data-d1-req-003.json +18 -0
  347. package/blueprints/persistence-data-d1/contributions/requirements/persistence-data-d1-req-004.json +18 -0
  348. package/blueprints/persistence-data-d1/contributions/requirements/persistence-data-d1-req-005.json +18 -0
  349. package/blueprints/persistence-data-d1/contributions/requirements/persistence-data-d1-req-006.json +18 -0
  350. package/blueprints/persistence-data-d1/contributions/requirements/persistence-data-d1-req-007.json +18 -0
  351. package/blueprints/persistence-data-d1/contributions/tacs/tac-1401-persistence-data-d1-facade.json +51 -0
  352. package/blueprints/persistence-data-d1/contributions/tacs/tac-1402-persistence-data-d1-migration-catalog.json +31 -0
  353. package/blueprints/persistence-data-d1/contributions/tacs/tac-1403-persistence-data-d1-deploy-gate.json +38 -0
  354. package/blueprints/persistence-data-d1/contributions/tacs/tac-1404-persistence-data-d1-recovery-runner.json +39 -0
  355. package/blueprints/persistence-data-d1/contributions/user-stories/persistence-data-d1-us-13101.json +45 -0
  356. package/blueprints/persistence-data-d1/contributions/user-stories/persistence-data-d1-us-13102.json +45 -0
  357. package/blueprints/persistence-data-d1/contributions/user-stories/persistence-data-d1-us-13103.json +46 -0
  358. package/blueprints/persistence-data-d1/contributions/user-stories/persistence-data-d1-us-13104.json +45 -0
  359. package/blueprints/persistence-data-d1/contributions/user-stories/persistence-data-d1-us-13105.json +45 -0
  360. package/blueprints/persistence-data-d1/contributions/user-stories/persistence-data-d1-us-13106.json +46 -0
  361. package/blueprints/persistence-data-d1/contributions/user-stories/persistence-data-d1-us-13107.json +38 -0
  362. package/blueprints/persistence-data-d1/docs/topics.md +59 -0
  363. package/blueprints/persistence-data-d1/guide/persistence-data-d1.md +77 -0
  364. package/blueprints/persistence-data-sqlite/README.md +50 -0
  365. package/blueprints/persistence-data-sqlite/assets/backup-procedures/hot-checkpoint-note.md +22 -0
  366. package/blueprints/persistence-data-sqlite/assets/backup-procedures/sqlite-file-copy.md +70 -0
  367. package/blueprints/persistence-data-sqlite/assets/schema-samples/catalog-file-layout.md +73 -0
  368. package/blueprints/persistence-data-sqlite/assets/schema-samples/migration-shape.md +76 -0
  369. package/blueprints/persistence-data-sqlite/blueprint.json +50 -0
  370. package/blueprints/persistence-data-sqlite/contributions/adrs/adr-601-persistence-data-sqlite-store-model.json +30 -0
  371. package/blueprints/persistence-data-sqlite/contributions/adrs/adr-602-persistence-data-sqlite-migration-discipline.json +30 -0
  372. package/blueprints/persistence-data-sqlite/contributions/adrs/adr-603-persistence-data-sqlite-event-secrecy.json +25 -0
  373. package/blueprints/persistence-data-sqlite/contributions/adrs/adr-604-persistence-data-sqlite-store-boundary.json +25 -0
  374. package/blueprints/persistence-data-sqlite/contributions/adrs/adr-605-persistence-data-sqlite-backup-model.json +25 -0
  375. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-001.json +18 -0
  376. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-002.json +18 -0
  377. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-003.json +18 -0
  378. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-004.json +18 -0
  379. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-005.json +18 -0
  380. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-006.json +18 -0
  381. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-007.json +18 -0
  382. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-008.json +18 -0
  383. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-009.json +18 -0
  384. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-010.json +18 -0
  385. package/blueprints/persistence-data-sqlite/contributions/requirements/persistence-data-sqlite-req-011.json +18 -0
  386. package/blueprints/persistence-data-sqlite/contributions/tacs/tac-601-persistence-data-sqlite-store.json +57 -0
  387. package/blueprints/persistence-data-sqlite/contributions/tacs/tac-602-persistence-data-sqlite-migration-runner.json +34 -0
  388. package/blueprints/persistence-data-sqlite/contributions/tacs/tac-603-persistence-data-sqlite-migration-catalog.json +31 -0
  389. package/blueprints/persistence-data-sqlite/contributions/tacs/tac-604-persistence-data-sqlite-backup-runner.json +33 -0
  390. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5101.json +46 -0
  391. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5102.json +46 -0
  392. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5103.json +37 -0
  393. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5104.json +45 -0
  394. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5105.json +45 -0
  395. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5106.json +46 -0
  396. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5107.json +45 -0
  397. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5108.json +46 -0
  398. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5109.json +46 -0
  399. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5110.json +37 -0
  400. package/blueprints/persistence-data-sqlite/contributions/user-stories/persistence-data-sqlite-us-5111.json +37 -0
  401. package/blueprints/persistence-data-sqlite/docs/topics.md +49 -0
  402. package/blueprints/persistence-data-sqlite/guide/persistence-data-sqlite.md +69 -0
  403. package/blueprints/security-auth-clerk/README.md +50 -0
  404. package/blueprints/security-auth-clerk/assets/authorisation/role-claim-mapping-sample.md +99 -0
  405. package/blueprints/security-auth-clerk/assets/middleware/node-middleware-shape.md +86 -0
  406. package/blueprints/security-auth-clerk/assets/wiring/clerk-provider-react.md +48 -0
  407. package/blueprints/security-auth-clerk/assets/wiring/clerk-provider-vue.md +41 -0
  408. package/blueprints/security-auth-clerk/blueprint.json +42 -0
  409. package/blueprints/security-auth-clerk/contributions/adrs/adr-1001-security-auth-clerk-auth-model.json +30 -0
  410. package/blueprints/security-auth-clerk/contributions/adrs/adr-1002-security-auth-clerk-middleware-boundary.json +25 -0
  411. package/blueprints/security-auth-clerk/contributions/adrs/adr-1003-security-auth-clerk-authorisation-adapter-contract.json +25 -0
  412. package/blueprints/security-auth-clerk/contributions/adrs/adr-1004-security-auth-clerk-claims-mapping-discipline.json +25 -0
  413. package/blueprints/security-auth-clerk/contributions/adrs/adr-1005-security-auth-clerk-session-lifecycle.json +25 -0
  414. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-001.json +18 -0
  415. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-002.json +18 -0
  416. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-003.json +18 -0
  417. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-004.json +18 -0
  418. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-005.json +18 -0
  419. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-006.json +18 -0
  420. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-007.json +18 -0
  421. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-008.json +18 -0
  422. package/blueprints/security-auth-clerk/contributions/requirements/security-auth-clerk-req-009.json +18 -0
  423. package/blueprints/security-auth-clerk/contributions/tacs/tac-1001-security-auth-clerk-middleware.json +42 -0
  424. package/blueprints/security-auth-clerk/contributions/tacs/tac-1002-security-auth-clerk-authorisation-adapter.json +30 -0
  425. package/blueprints/security-auth-clerk/contributions/tacs/tac-1003-security-auth-clerk-session-verifier.json +38 -0
  426. package/blueprints/security-auth-clerk/contributions/tacs/tac-1004-security-auth-clerk-claims-mapper.json +22 -0
  427. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9101.json +46 -0
  428. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9102.json +38 -0
  429. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9103.json +46 -0
  430. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9104.json +46 -0
  431. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9105.json +36 -0
  432. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9106.json +36 -0
  433. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9107.json +37 -0
  434. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9108.json +36 -0
  435. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9109.json +36 -0
  436. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9110.json +37 -0
  437. package/blueprints/security-auth-clerk/contributions/user-stories/security-auth-clerk-us-9111.json +36 -0
  438. package/blueprints/security-auth-clerk/docs/topics.md +56 -0
  439. package/blueprints/security-auth-clerk/guide/security-auth-clerk.md +71 -0
  440. package/blueprints/security-auth-keycloak/README.md +56 -0
  441. package/blueprints/security-auth-keycloak/assets/local-container/keycloak-bootstrap.md +54 -0
  442. package/blueprints/security-auth-keycloak/assets/middleware/verifier-wiring.md +93 -0
  443. package/blueprints/security-auth-keycloak/assets/mock-introspection/mock-introspection-shape.md +62 -0
  444. package/blueprints/security-auth-keycloak/assets/provider-router/router-shape.md +71 -0
  445. package/blueprints/security-auth-keycloak/assets/realm-skeleton/realm-config-shape.md +55 -0
  446. package/blueprints/security-auth-keycloak/blueprint.json +49 -0
  447. package/blueprints/security-auth-keycloak/contributions/adrs/adr-1201-security-auth-keycloak-auth-model.json +35 -0
  448. package/blueprints/security-auth-keycloak/contributions/adrs/adr-1202-security-auth-keycloak-verification-mode-choice.json +30 -0
  449. package/blueprints/security-auth-keycloak/contributions/adrs/adr-1203-security-auth-keycloak-provider-routing-seam.json +30 -0
  450. package/blueprints/security-auth-keycloak/contributions/adrs/adr-1204-security-auth-keycloak-session-vs-token-contract.json +25 -0
  451. package/blueprints/security-auth-keycloak/contributions/adrs/adr-1205-security-auth-keycloak-jwks-rotation-cache-lifetime.json +30 -0
  452. package/blueprints/security-auth-keycloak/contributions/adrs/adr-1206-security-auth-keycloak-refresh-and-sign-out-posture.json +25 -0
  453. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-001.json +18 -0
  454. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-002.json +18 -0
  455. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-003.json +18 -0
  456. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-004.json +18 -0
  457. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-005.json +18 -0
  458. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-006.json +18 -0
  459. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-007.json +18 -0
  460. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-008.json +18 -0
  461. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-009.json +18 -0
  462. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-010.json +18 -0
  463. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-011.json +18 -0
  464. package/blueprints/security-auth-keycloak/contributions/requirements/security-auth-keycloak-req-012.json +18 -0
  465. package/blueprints/security-auth-keycloak/contributions/tacs/tac-1201-security-auth-keycloak-discovery-client.json +51 -0
  466. package/blueprints/security-auth-keycloak/contributions/tacs/tac-1202-security-auth-keycloak-jwt-verifier.json +46 -0
  467. package/blueprints/security-auth-keycloak/contributions/tacs/tac-1203-security-auth-keycloak-introspection-client.json +45 -0
  468. package/blueprints/security-auth-keycloak/contributions/tacs/tac-1204-security-auth-keycloak-provider-router.json +37 -0
  469. package/blueprints/security-auth-keycloak/contributions/tacs/tac-1205-security-auth-keycloak-role-adapter.json +32 -0
  470. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11101.json +47 -0
  471. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11102.json +45 -0
  472. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11103.json +36 -0
  473. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11104.json +36 -0
  474. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11105.json +45 -0
  475. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11106.json +46 -0
  476. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11107.json +45 -0
  477. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11108.json +45 -0
  478. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11109.json +37 -0
  479. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11110.json +45 -0
  480. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11111.json +45 -0
  481. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11112.json +37 -0
  482. package/blueprints/security-auth-keycloak/contributions/user-stories/security-auth-keycloak-us-11113.json +36 -0
  483. package/blueprints/security-auth-keycloak/docs/topics.md +74 -0
  484. package/blueprints/security-auth-keycloak/guide/security-auth-keycloak.md +81 -0
  485. package/blueprints/security-auth-magic-link/README.md +46 -0
  486. package/blueprints/security-auth-magic-link/assets/email-templates/magic-link-email.md +44 -0
  487. package/blueprints/security-auth-magic-link/assets/email-templates/stub-adapter.md +55 -0
  488. package/blueprints/security-auth-magic-link/assets/principal-registry-samples/allow-list-file.md +46 -0
  489. package/blueprints/security-auth-magic-link/assets/principal-registry-samples/single-address.md +35 -0
  490. package/blueprints/security-auth-magic-link/blueprint.json +45 -0
  491. package/blueprints/security-auth-magic-link/contributions/adrs/adr-501-security-auth-magic-link-model.json +30 -0
  492. package/blueprints/security-auth-magic-link/contributions/adrs/adr-502-security-auth-magic-link-token-secrecy.json +25 -0
  493. package/blueprints/security-auth-magic-link/contributions/adrs/adr-503-security-auth-magic-link-anti-enumeration-budget.json +25 -0
  494. package/blueprints/security-auth-magic-link/contributions/adrs/adr-504-security-auth-magic-link-session-lifecycle.json +30 -0
  495. package/blueprints/security-auth-magic-link/contributions/adrs/adr-505-security-auth-magic-link-rate-limiting.json +25 -0
  496. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-001.json +18 -0
  497. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-002.json +18 -0
  498. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-003.json +18 -0
  499. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-004.json +18 -0
  500. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-005.json +18 -0
  501. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-006.json +18 -0
  502. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-007.json +18 -0
  503. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-008.json +18 -0
  504. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-009.json +18 -0
  505. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-010.json +18 -0
  506. package/blueprints/security-auth-magic-link/contributions/requirements/security-auth-magic-link-req-011.json +18 -0
  507. package/blueprints/security-auth-magic-link/contributions/tacs/tac-501-security-auth-magic-link-manager.json +45 -0
  508. package/blueprints/security-auth-magic-link/contributions/tacs/tac-502-security-auth-magic-link-session-manager.json +46 -0
  509. package/blueprints/security-auth-magic-link/contributions/tacs/tac-503-security-auth-magic-link-login-routes.json +76 -0
  510. package/blueprints/security-auth-magic-link/contributions/tacs/tac-504-security-auth-magic-link-email-delivery-adapter.json +26 -0
  511. package/blueprints/security-auth-magic-link/contributions/tacs/tac-505-security-auth-magic-link-principal-registry.json +26 -0
  512. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3101.json +64 -0
  513. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3102.json +54 -0
  514. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3103.json +45 -0
  515. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3104.json +55 -0
  516. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3105.json +46 -0
  517. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3106.json +46 -0
  518. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3107.json +54 -0
  519. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3108.json +46 -0
  520. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3109.json +48 -0
  521. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3110.json +45 -0
  522. package/blueprints/security-auth-magic-link/contributions/user-stories/security-auth-magic-link-us-3111.json +45 -0
  523. package/blueprints/security-auth-magic-link/docs/topics.md +45 -0
  524. package/blueprints/security-auth-magic-link/guide/security-auth-magic-link.md +65 -0
  525. package/blueprints/security-auth-oauth2/README.md +50 -0
  526. package/blueprints/security-auth-oauth2/assets/mock-provider/mock-oidc-shape.md +55 -0
  527. package/blueprints/security-auth-oauth2/assets/provider-selector/list-shape.md +27 -0
  528. package/blueprints/security-auth-oauth2/assets/providers/generic-oidc-discovery.md +31 -0
  529. package/blueprints/security-auth-oauth2/assets/providers/github-oauth2-only.md +31 -0
  530. package/blueprints/security-auth-oauth2/assets/providers/google-oidc.md +31 -0
  531. package/blueprints/security-auth-oauth2/assets/session-bridge/opaque-handle-shape.md +59 -0
  532. package/blueprints/security-auth-oauth2/blueprint.json +44 -0
  533. package/blueprints/security-auth-oauth2/contributions/adrs/adr-1101-security-auth-oauth2-auth-model.json +35 -0
  534. package/blueprints/security-auth-oauth2/contributions/adrs/adr-1102-security-auth-oauth2-provider-abstraction-contract.json +25 -0
  535. package/blueprints/security-auth-oauth2/contributions/adrs/adr-1103-security-auth-oauth2-pkce-discipline.json +25 -0
  536. package/blueprints/security-auth-oauth2/contributions/adrs/adr-1104-security-auth-oauth2-session-bridge-shape.json +25 -0
  537. package/blueprints/security-auth-oauth2/contributions/adrs/adr-1105-security-auth-oauth2-refresh-token-posture.json +25 -0
  538. package/blueprints/security-auth-oauth2/contributions/adrs/adr-1106-security-auth-oauth2-multi-provider-routing.json +25 -0
  539. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-001.json +18 -0
  540. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-002.json +18 -0
  541. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-003.json +18 -0
  542. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-004.json +18 -0
  543. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-005.json +18 -0
  544. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-006.json +18 -0
  545. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-007.json +18 -0
  546. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-008.json +18 -0
  547. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-009.json +18 -0
  548. package/blueprints/security-auth-oauth2/contributions/requirements/security-auth-oauth2-req-010.json +18 -0
  549. package/blueprints/security-auth-oauth2/contributions/tacs/tac-1101-security-auth-oauth2-flow-controller.json +61 -0
  550. package/blueprints/security-auth-oauth2/contributions/tacs/tac-1102-security-auth-oauth2-provider-adapter.json +33 -0
  551. package/blueprints/security-auth-oauth2/contributions/tacs/tac-1103-security-auth-oauth2-session-bridge.json +51 -0
  552. package/blueprints/security-auth-oauth2/contributions/tacs/tac-1104-security-auth-oauth2-provider-selector.json +29 -0
  553. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10101.json +47 -0
  554. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10102.json +45 -0
  555. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10103.json +36 -0
  556. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10104.json +36 -0
  557. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10105.json +55 -0
  558. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10106.json +45 -0
  559. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10107.json +46 -0
  560. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10108.json +46 -0
  561. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10109.json +37 -0
  562. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10110.json +37 -0
  563. package/blueprints/security-auth-oauth2/contributions/user-stories/security-auth-oauth2-us-10111.json +28 -0
  564. package/blueprints/security-auth-oauth2/docs/topics.md +58 -0
  565. package/blueprints/security-auth-oauth2/guide/security-auth-oauth2.md +65 -0
  566. package/blueprints/security-secrets-management/README.md +48 -0
  567. package/blueprints/security-secrets-management/assets/cli-usage/agent-access-pattern.md +69 -0
  568. package/blueprints/security-secrets-management/assets/manifest-samples/environment-aware-example.md +33 -0
  569. package/blueprints/security-secrets-management/assets/manifest-samples/secrets-yaml-shape.md +74 -0
  570. package/blueprints/security-secrets-management/assets/ui-integration/three-way-choice.md +44 -0
  571. package/blueprints/security-secrets-management/blueprint.json +43 -0
  572. package/blueprints/security-secrets-management/contributions/adrs/adr-901-security-secrets-management-secrets-source.json +30 -0
  573. package/blueprints/security-secrets-management/contributions/adrs/adr-902-security-secrets-management-default-vendor.json +35 -0
  574. package/blueprints/security-secrets-management/contributions/adrs/adr-903-security-secrets-management-agent-access-discipline.json +25 -0
  575. package/blueprints/security-secrets-management/contributions/adrs/adr-904-security-secrets-management-dotenv-reflection.json +25 -0
  576. package/blueprints/security-secrets-management/contributions/adrs/adr-905-security-secrets-management-rotation-and-audit.json +30 -0
  577. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-001.json +18 -0
  578. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-002.json +18 -0
  579. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-003.json +18 -0
  580. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-004.json +18 -0
  581. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-005.json +18 -0
  582. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-006.json +18 -0
  583. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-007.json +18 -0
  584. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-008.json +18 -0
  585. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-009.json +18 -0
  586. package/blueprints/security-secrets-management/contributions/requirements/security-secrets-management-req-010.json +18 -0
  587. package/blueprints/security-secrets-management/contributions/tacs/tac-901-security-secrets-management-manager-client.json +52 -0
  588. package/blueprints/security-secrets-management/contributions/tacs/tac-902-security-secrets-management-manifest.json +34 -0
  589. package/blueprints/security-secrets-management/contributions/tacs/tac-903-security-secrets-management-env-reflector.json +46 -0
  590. package/blueprints/security-secrets-management/contributions/tacs/tac-904-security-secrets-management-rotation-gate.json +57 -0
  591. package/blueprints/security-secrets-management/contributions/tacs/tac-905-security-secrets-management-agent-cli.json +59 -0
  592. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8101.json +46 -0
  593. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8102.json +45 -0
  594. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8103.json +37 -0
  595. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8104.json +45 -0
  596. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8105.json +55 -0
  597. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8106.json +46 -0
  598. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8107.json +46 -0
  599. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8108.json +45 -0
  600. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8109.json +46 -0
  601. package/blueprints/security-secrets-management/contributions/user-stories/security-secrets-management-us-8110.json +45 -0
  602. package/blueprints/security-secrets-management/docs/topics.md +46 -0
  603. package/blueprints/security-secrets-management/guide/security-secrets-management.md +68 -0
  604. package/fixtures/canary-manifest.json +1 -101
  605. package/package.json +15 -9
  606. package/rcf/.identity/profile.md +37 -0
  607. package/rcf/knowledge/INDEX.md +12 -0
  608. package/rcf/knowledge/README.md +41 -0
  609. package/rcf/knowledge/docs/.gitkeep +0 -0
  610. package/rcf/knowledge/notes/.gitkeep +0 -0
  611. package/scripts/preinstall-node-check.mjs +61 -0
  612. package/src/blueprint/apply.js +12 -6
  613. package/src/blueprint/conflicts.js +8 -8
  614. package/src/blueprint/index.js +2 -1
  615. package/src/blueprint/list.js +60 -0
  616. package/src/blueprint/loader.js +26 -1
  617. package/src/blueprint/namespace.js +20 -16
  618. package/src/blueprint/shelf-resolver.js +173 -0
  619. package/src/blueprint/supersede.js +3 -2
  620. package/src/browser-verify/invariants.js +40 -0
  621. package/src/browser-verify/runner.js +2 -0
  622. package/src/cli/blueprint.js +53 -9
  623. package/src/cli/init.js +2 -0
  624. package/src/core/store/writer.js +64 -2
  625. package/src/setup/agent-setup.js +28 -10
@@ -0,0 +1,30 @@
1
+ {
2
+ "adrId": "ADR-1203-security-auth-keycloak-provider-routing-seam",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "A provider-routing seam is contributed at one named point in the request chain; the seam is described in generic OAuth2/OIDC vocabulary and does not commit to any second issuer",
8
+ "context": "A project may sit in a deployment where a receiving system's request stream carries tokens issued by more than one server. Two Keycloak realms is one shape; a Keycloak realm alongside a project-authored second-issuer path (a corporate OIDC provider the operator declares separately, a second Keycloak realm on a different hostname, an internal service-to-service issuer) is another. The blueprint stays vendor-committed to Keycloak; it does not know, and refuses to encode, what the second issuer is. What the blueprint does know is that a deployment with more than one issuer needs the routing decision made in exactly one legible place, not scattered across handlers where the ship gate cannot see it.",
9
+ "decision": "The blueprint contributes TAC-1204, the provider-router, as the seam a project's own middleware invokes on every authenticated request. The seam's contract is a pure function: `route(request) -> { realmSlug, verificationMode } | notThisIssuer`. A project with only Keycloak configured wires the seam to a constant realmSlug and the mechanism is single-branched. A project with two Keycloak realms wires the seam to inspect a request feature (a header the project's middleware layer already reads, a request-path prefix the project owns, a preflight claim the project extracts elsewhere) and picks the realmSlug. A project with Keycloak plus a non-Keycloak issuer wires the seam to return `notThisIssuer` when the routing decision hands off to a middleware layer above this blueprint's mechanism reach; the middleware layer above is the project's own concern, not the blueprint's. The blueprint refuses to name the header, the path, the claim, or the second issuer; the ratified reshape ruling forbids naming a specific dual-provider deployment shape, and the seam contract respects that.",
10
+ "consequences": "A project that has one Keycloak realm pays zero cost for the seam: the wiring is a constant. A project that has two Keycloak realms wires the routing once and every request passes through it observably. A project that has Keycloak alongside another issuer has one named point where the choice is made, which is a paper-trailed decision the ship gate can see. The trade against a hard-coded assumption (only Keycloak, only one realm, always JWKS mode) is one function call on the request path; that cost is trivial and the observability payoff scales with deployment complexity. The blueprint never names a second issuer; a project that has one owns that decision on a project-level ADR whose `related-reqs` cites this ADR.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "Refuse to contribute any routing seam; assume Keycloak is the only issuer forever",
14
+ "summary": "The mechanism reads every access token against the single configured realm; a request carrying a token from a different issuer refuses uniformly.",
15
+ "reasonNotChosen": "Bakes an assumption into the mechanism that the ratified reshape ruling names as the exact deployment shape the blueprint must be aware of. A project with a second issuer would either hand-wire the choice above the mechanism (invisible) or push it into every handler (unmaintainable). The seam is cheap and makes the decision legible."
16
+ },
17
+ {
18
+ "name": "Name a specific second-issuer integration in the mechanism (e.g., `providerLayer: 'keycloak' | 'other'`)",
19
+ "summary": "Ship an ADR (and the router TAC) that names the second issuer's shape.",
20
+ "reasonNotChosen": "Fights the ratified reshape ruling. The candidate that minted this blueprint originally proposed naming a specific dual-provider deployment, and the ruling stripped that; the seam stays in vendor-neutral OAuth2/OIDC vocabulary. A project that has a second issuer names it on its own ADR, not on the blueprint's."
21
+ },
22
+ {
23
+ "name": "Publish the seam as a per-issuer array and let the project fill it with N records",
24
+ "summary": "The blueprint declares a `configuredIssuers: []` array; each record is a full realm-record (or a similar shape); the router iterates.",
25
+ "reasonNotChosen": "Turns the seam from a decision point into a data structure; the mechanism ends up owning the routing algorithm as well as the data, which is exactly the responsibility the ADR is trying to keep on the project side. The pure-function contract keeps the algorithm the project's concern."
26
+ }
27
+ ],
28
+ "createdAt": "2026-08-31T00:00:00Z",
29
+ "updatedAt": "2026-08-31T00:00:00Z"
30
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "adrId": "ADR-1204-security-auth-keycloak-session-vs-token-contract",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Keycloak owns tokens; the project chooses opaque-handle session or stateless bearer per realm record",
8
+ "context": "Keycloak's ownership of the token stream is not negotiable within this blueprint's vendor commitment; the realm mints access, id, and refresh tokens and the project verifies them. What the project can choose is the shape of any application session it layers on top. The vendor-neutral sibling committed to opaque-handle sessions uniformly because it could not rely on the underlying provider for revocation; a Keycloak-committed blueprint has the realm's revocation clock available and can honestly support a stateless posture where the operator prefers it.",
9
+ "decision": "The realm record carries a `sessionMode` field with two legal values: `opaque-handle` (the default) and `stateless`. Under `opaque-handle`, the session-bridge issues an opaque-handle cookie (`Secure`, `HttpOnly`, `SameSite=Lax`); the server-side session record stores the Keycloak-minted access token, id_token, and (if issued) refresh token keyed by the handle; the browser never holds a provider-minted token; the reduced Principal is what handlers read from `request.auth`. Under `stateless`, no server-side session record is kept; every request re-verifies the access token from a bearer source the operator declares on the record (`bearerSource: 'authorization-header' | 'cookie:<name>'`); the reduced Principal is derived per request from the verified token and no session cookie is issued by the mechanism (a project may still issue its own out-of-band session for unrelated reasons, but the auth mechanism does not). Provider tokens are never exposed on `request.auth` in either mode; only the reduced Principal (`principalId`, `provider`, `email?`, `roles[]`, `organisationIds[]`) crosses outward.",
10
+ "consequences": "The reference wiring is opaque-handle: it works out of the box on any Node HTTP framework, matches the sibling blueprints' pattern, and gives the project cheap sign-out (one delete on the session record). The stateless posture matches deployments where the operator has committed to bearer-per-request verification (a service tier, a public API surface behind an API gateway that itself terminates the browser session, a strict-no-cookie environment). Naming the mode on the record makes the trade a paper-trailed decision. The two modes are not composable per realm record; a project that needs both patterns on different routes declares two realm records (potentially against the same realm) with distinct `sessionMode` values and routes through the provider-router seam.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "Opaque-handle only (mirror the sibling blueprints uniformly)",
14
+ "summary": "Every session is server-side; no stateless posture.",
15
+ "reasonNotChosen": "Fights the operational reason a project might choose Keycloak in the first place. A deployment on a stateless service tier that wants Keycloak-issued JWTs verified per request and no cookie shape is a legitimate posture; refusing it in the mechanism would drive that project to hand-wire the verification outside the blueprint and lose the ship-gate observability."
16
+ },
17
+ {
18
+ "name": "Stateless only (JWT in cookie, browser holds the JWT)",
19
+ "summary": "Every request carries the Keycloak-minted JWT in a cookie; no server-side session record.",
20
+ "reasonNotChosen": "Leaks the provider token surface into every browser side-channel; a compromised cookie is a lifted Keycloak token whose only revocation clock is the realm's expiry timer. Opaque-handle is the safer default for the common small-project deployment shape."
21
+ }
22
+ ],
23
+ "createdAt": "2026-08-31T00:00:00Z",
24
+ "updatedAt": "2026-08-31T00:00:00Z"
25
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "adrId": "ADR-1205-security-auth-keycloak-jwks-rotation-cache-lifetime",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "JWKS cache TTL is 900 seconds by default with kid-miss refresh; the operator raises or lowers on the realm record",
8
+ "context": "JWKS caching sits on the hot path of JWT verification: too short a TTL means unnecessary network round-trips against the realm on every verification; too long a TTL means a key rotation the realm has published takes too long to reach the verifier, which either delays revocation or produces silent verification failures when the old key ages out and no cached entry exists for the new one. Two mechanisms defend against the second failure mode: a TTL that bounds the maximum staleness, and a kid-miss refresh that recovers when the realm has rotated inside the TTL window.",
9
+ "decision": "The default TTL for a cached JWKS response is 900 seconds (15 minutes). The realm record MAY declare `jwksCacheTtlSeconds` to raise or lower this; values below 60 are refused at boot (the network amplification would be self-defeating) and values above 86400 are refused at boot (staleness beyond a day risks losing rotations for too long). Every verification path first consults the cache; on a kid-miss (the token's `kid` does not appear in the cached JWKS), the verifier triggers exactly one refresh from `jwks_uri`; if the refresh succeeds and the new JWKS carries the kid, verification proceeds; if the refresh succeeds and the new JWKS still does not carry the kid, the token is refused with `KEYCLOAK_JWT_KID_UNKNOWN`; if the refresh itself fails, the token is refused with `KEYCLOAK_JWKS_UNREACHABLE`. A kid-miss triggers at most one refresh per token verification; a flood of unknown-kid tokens does not multiply into a flood of JWKS fetches.",
10
+ "consequences": "A key rotation the realm has published propagates within one TTL window at the worst case and within one verification at the best case (a kid-miss refresh recovers immediately). The trade against a shorter TTL (say 60 seconds) is the network round-trip cost; against a longer TTL (say 3600 seconds) is the maximum staleness window. The one-refresh-per-verification cap defends the realm's JWKS endpoint against a token-based amplification attack. The refuse-on-unknown-kid-after-refresh posture is what catches genuine forgery attempts (an attacker who signs with a key the realm never published sees exactly one JWKS refresh and then a stable-coded refusal).",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "No cache; fetch JWKS on every verification",
14
+ "summary": "Every request triggers one JWKS fetch.",
15
+ "reasonNotChosen": "Trivial to implement and unacceptable in production. The realm's JWKS endpoint would carry a request rate equal to the project's verification rate; on a small project that is fine and on any larger deployment it is untenable."
16
+ },
17
+ {
18
+ "name": "Cache forever; refresh only on kid-miss",
19
+ "summary": "No TTL; only a token whose kid is unknown triggers a refresh.",
20
+ "reasonNotChosen": "Loses the bound-staleness property. A realm that has rotated but that has not yet issued a token under the new key (a common state for the first few minutes after rotation) leaves the verifier holding a cache no signal will refresh. The TTL is the belt against that class of missed-rotation state."
21
+ },
22
+ {
23
+ "name": "Push-based invalidation (webhook the realm calls into the project)",
24
+ "summary": "The realm notifies the project when JWKS rotates.",
25
+ "reasonNotChosen": "Not a Keycloak feature at v1 of this blueprint; would require project-authored plumbing on both sides. A pull-with-TTL model is the standard shape and Keycloak matches it verbatim."
26
+ }
27
+ ],
28
+ "createdAt": "2026-08-31T00:00:00Z",
29
+ "updatedAt": "2026-08-31T00:00:00Z"
30
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "adrId": "ADR-1206-security-auth-keycloak-refresh-and-sign-out-posture",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "accepted",
7
+ "title": "Refresh follows the realm's rotation model; sign-out clears the project session and offers realm-side end-session as a per-record option",
8
+ "context": "Two operational decisions sit adjacent to the identity flow: how refresh tokens rotate, and how sign-out relates to the realm session. Keycloak's admin surface already exposes controls for both (revoke-refresh-token, refresh-token-max-reuse, and the RP-initiated logout endpoint on the realm's discovery), so the blueprint's role is to honour the realm's declared behaviour rather than to layer a second policy.",
9
+ "decision": "Refresh: the flow controller presents the current stored refresh token on every refresh call; a 2xx response replaces the stored value with the new one; a 4xx `invalid_grant` on refresh is treated as a session-invalidation signal and the flow controller invalidates the session handle, clears the associated refresh-token chain, and emits `keycloak.refresh_invalidated`. The blueprint does NOT layer a reuse-detection policy above what the realm already does; the realm's `Revoke Refresh Token` and `Refresh Token Max Reuse` settings ARE the mechanism, and the blueprint's role is to fail cleanly when the realm signals invalidation. Sign-out: a POST to the project's sign-out route invalidates the project session handle (opaque-handle mode) or clears the bearer-source cookie (stateless mode); the response clears the session cookie in either case. When the realm record sets `providerLogout: 'redirect'` and the discovery record carries `end_session_endpoint`, the sign-out response is a 302 to that endpoint with `id_token_hint` carrying the raw id_token stored on the session (opaque-handle) or the last-verified id_token the operator's bearer source has cached (stateless). A record that sets `providerLogout: 'redirect'` on a realm whose discovery does not publish `end_session_endpoint` is refused at boot.",
10
+ "consequences": "Refresh behaviour is legible from the realm's admin console; a project that wants stricter reuse detection tightens the realm setting rather than editing project code. Sign-out is one delete on the session record (opaque-handle mode) or one cookie-clear (stateless mode); the realm-side end-session is opt-in and paper-trailed on the record. The trade against re-implementing rotation policy above the realm is that a realm configured to allow reuse permissively will not be tightened by the project's mechanism; that is intended - the operator authored the realm and the project honours it, not the other way around. A boot refusal on `providerLogout: 'redirect'` without `end_session_endpoint` catches the misconfiguration where a project believes it is doing realm-side sign-out and silently is not.",
11
+ "alternativesConsidered": [
12
+ {
13
+ "name": "Enforce reuse-detection in the blueprint regardless of realm setting",
14
+ "summary": "The flow controller tracks the previous refresh-token value; presenting it twice revokes the chain even when the realm would not.",
15
+ "reasonNotChosen": "Fights the realm's declared policy. A project whose operator has deliberately relaxed reuse behaviour for their environment should not be quietly tightened by the mechanism; the realm is the source of truth for that class of decision."
16
+ },
17
+ {
18
+ "name": "Mandate realm-side sign-out on every project sign-out",
19
+ "summary": "Every sign-out redirects to `end_session_endpoint` regardless of the record's `providerLogout` field.",
20
+ "reasonNotChosen": "Fights half the deployments. Some operators legitimately want the realm session to persist across a project sign-out (the operator's other applications on the same realm stay signed in); mandating realm-side sign-out here would drive that operator to disable the sign-out route entirely, which is worse than the per-record posture."
21
+ }
22
+ ],
23
+ "createdAt": "2026-08-31T00:00:00Z",
24
+ "updatedAt": "2026-08-31T00:00:00Z"
25
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-001",
3
+ "prdId": "PRD-001",
4
+ "title": "A Keycloak realm is the identity source; the project federates through OIDC authorisation-code with PKCE",
5
+ "description": "The project's authentication model is delegated identity to a Keycloak realm the operator runs (locally at development time, in a project-owned deployment at operate time). Identity is proven by a completed OAuth2 authorisation-code round-trip with PKCE (RFC 7636, S256) against the realm's OIDC endpoints; the id_token issued by the realm carries the authenticated subject. The wire vocabulary is OIDC on top of OAuth2 and the flow shape is authorisation-code + PKCE; the vendor commitment is Keycloak. The blueprint contributes Keycloak-shaped decisions the vendor-neutral sibling `security-auth-oauth2` deliberately does not fix: a Keycloak realm as the concrete issuer, a Keycloak client with a chosen verification mode (JWT via JWKS, or opaque via the RFC 7662 introspection endpoint), the Keycloak claim shape for client roles, and the local-first stand-up shape through the vendor's official container image.",
6
+ "category": "functional",
7
+ "domain": "authentication",
8
+ "priority": "must",
9
+ "rationale": "Two commitments only make sense together. Fixing the flow shape (authorisation-code + PKCE) keeps the state machine legible and defends against front-channel code interception; fixing the vendor (Keycloak) lets the project reason about a concrete realm surface, a concrete admin console, and a concrete container image at operate time. A project that wants vendor-neutrality picks the sibling; a project that has committed to running a Keycloak realm gets the Keycloak-shaped decisions the sibling deliberately leaves open.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:01-flow-shape"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-002",
3
+ "prdId": "PRD-001",
4
+ "title": "OIDC discovery against the Keycloak realm's well-known endpoint drives the endpoint record",
5
+ "description": "The project reads the realm's discovery document from `<issuerBaseUrl>/realms/<realm>/.well-known/openid-configuration` at boot (or at a cached refresh cadence when the operator prefers a background refresh) and populates the realm record's endpoint fields from it: `authorization_endpoint`, `token_endpoint`, `userinfo_endpoint`, `jwks_uri`, `introspection_endpoint`, `end_session_endpoint`, `issuer`. The record shape the project maintains carries `{ realmSlug, issuerBaseUrl, realmName, clientId, clientSecret (server-only), verificationMode ('jwks' | 'introspection'), roleClaimShape ('client-roles' | 'realm-roles'), scopes[], redirectUri, providerLogout ('redirect' | 'none') }`. A realm whose discovery fetch fails at boot follows the fail-safe posture in REQ-011; a realm whose discovery response omits any endpoint the chosen `verificationMode` depends on is refused at boot with a stable-coded error naming the missing field.",
6
+ "category": "functional",
7
+ "domain": "authentication",
8
+ "priority": "must",
9
+ "rationale": "Keycloak publishes discovery as a matter of course; a project that reads it at boot inherits the realm's current endpoint shape without having to encode a Keycloak URL layout in project code. A realm operator who moves the deployment or reshapes the URL path (a valid Keycloak configuration option through the realm's `hostname-*` settings) does not require a project code change; the discovery pull refreshes the record. Fail-fast on missing endpoints catches an operator who has enabled JWKS-mode against a client whose realm does not publish `jwks_uri` (a rare misconfiguration but a real one).",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:02-discovery"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-003",
3
+ "prdId": "PRD-001",
4
+ "title": "JWT verification via JWKS with a rotation cache is the default verification mode",
5
+ "description": "When the realm record's `verificationMode` is `jwks` (the default), the JWT verifier reads the realm's public key set from the `jwks_uri` on the discovery record and validates every access token (and every id_token) against it before any session issue or downstream authorisation check. Verification checks: signature valid under a key from JWKS resolved by the token's `kid`; `iss` equality against the discovery record's `issuer`; `aud` containment of the record's `clientId` (or, for realm-scoped tokens whose `azp` names the client, `azp` equality); `exp` in the future by at least one second; `nbf` not in the future; `iat` in the past by no more than `idTokenMaxAgeSeconds` (default 3600). The JWKS is cached by realm slug with a TTL declared on `jwksCacheTtlSeconds` (default 900, see ADR-1205); a `kid` miss triggers exactly one refresh before refusing the token, so a key rotation the realm has published propagates without a process restart. Cache entries are refreshed on TTL expiry from the head of the queue; no verification path blocks on JWKS fetch when a cached entry is still valid.",
6
+ "category": "functional",
7
+ "domain": "security",
8
+ "priority": "must",
9
+ "rationale": "JWKS-based verification is cheap on the hot path and keeps the realm the sole custodian of signing material. A rotation cache with kid-miss refresh means the realm can roll keys on its own cadence and the project keeps verifying without downtime, without the project code path knowing anything about rotation timing. Fail-safe on kid-miss (one refresh, then refuse) prevents an attacker from flooding an unknown-kid token to force unbounded JWKS fetches.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:03-jwt-verify"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-004",
3
+ "prdId": "PRD-001",
4
+ "title": "Opaque-token introspection via the RFC 7662 endpoint is the alternate verification mode",
5
+ "description": "When the realm record's `verificationMode` is `introspection`, the verifier calls the realm's `introspection_endpoint` on every presented access token, presenting the record's `clientId` and `clientSecret` via HTTP basic auth per RFC 7662 section 2.1. A response with `active: true` is treated as verified and the response's claim record is the identity source; a response with `active: false` or a non-2xx status refuses the token with a stable-coded reason. Introspection is expensive on the hot path (one HTTP round-trip per verification) so the mode is opt-in per realm record; the introspection response MAY be short-lived cached under the record's `introspectionCacheTtlSeconds` (default 0 for no cache; the operator raises it when their realm's issuance rate makes the round-trip cost visible). This is the seam that lets the receiving system co-exist with another OAuth2 or OIDC provider a project's own middleware layer routes to: an introspection endpoint speaks the same RFC 7662 shape whether the issuer is Keycloak or some other conforming server, so a project may reach for the introspection path where its middleware has chosen to consult a non-Keycloak issuer without changing the vocabulary this blueprint owns.",
6
+ "category": "functional",
7
+ "domain": "security",
8
+ "priority": "must",
9
+ "rationale": "Some Keycloak client configurations issue opaque tokens rather than JWTs, and some projects prefer introspection for the stronger revocation guarantee (a JWT is valid until expiry; an introspection endpoint reflects revocations in real time). Making mode a per-record field keeps the mechanism symmetric across the two verification paths and lets a project mix modes across realms if the deployment has more than one. The RFC 7662 shape is standards-tracked and generic; the seam is described in that vocabulary rather than as an integration to any specific non-Keycloak issuer.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:04-introspection"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-005",
3
+ "prdId": "PRD-001",
4
+ "title": "A provider-routing seam lets a project's middleware decide which issuer to consult per request",
5
+ "description": "The blueprint contributes a provider-router seam (TAC-1204) that a project's own middleware layer invokes on every authenticated request to answer one question: for this request, which configured realm record (or, at the operator's option, which other OAuth2/OIDC issuer the project may have declared elsewhere in its own middleware chain) is the authoritative source of identity? The seam is a pure function on the request shape: `route(request) -> { realmSlug, verificationMode }` or the sentinel `notThisIssuer` when the routing decision hands off to a middleware layer above this blueprint's mechanism reach. The seam does NOT itself name any second issuer; the blueprint is Keycloak-committed and the seam's role is to make explicit the ONE point in the request-processing chain where a project that has chosen to consult two issuers can wire that decision. A project that has only Keycloak configured wires the seam to a constant realmSlug and the mechanism is unchanged. The routing decision is expressed in generic OAuth2/OIDC vocabulary (a header the middleware layer inspects, a request-path prefix, a claim on a preflight token) and refuses to embed any concrete deployment shape.",
6
+ "category": "functional",
7
+ "domain": "authentication",
8
+ "priority": "must",
9
+ "rationale": "A project may sit in a deployment where a receiving system's request stream carries tokens issued by more than one server. The blueprint stays vendor-committed to Keycloak: it does not name a second issuer, it does not describe a specific dual-provider integration, and it does not contribute an ADR that fixes what the second issuer is. What it does contribute is the seam a project's own middleware needs when it holds that decision: the SHAPE of the routing function and the exact seam location in the request-processing chain. Naming this seam explicitly, in generic vocabulary, is the ratified provider-aware install-design shape; without it, a project that had that need would hand-wire the decision somewhere in its handlers where the ship gate cannot see it, and every downstream reader of the codebase would guess where the routing lived.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:05-provider-routing"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-006",
3
+ "prdId": "PRD-001",
4
+ "title": "Role extraction reads from Keycloak's client-roles or realm-roles claim shape",
5
+ "description": "Keycloak places roles on two claim families the operator selects between when configuring the client: `resource_access.<clientId>.roles` (per-client roles, the recommended default) and `realm_access.roles` (realm-wide roles). The realm record's `roleClaimShape` field declares which family this client uses (`client-roles` or `realm-roles`); the role adapter (TAC-1205) reads roles from that path on every verified token and populates the reduced Principal's `roles[]` array with the resulting string list. A token whose expected claim path is absent yields an empty `roles[]` (not a refusal); a project that wants stricter behaviour (a specific claim path must be present, or a specific role must be present, or the request is refused) authors a project-side authoriser above the reduced Principal and does not push that policy into the mechanism. The role adapter does not remap Keycloak role strings into project-defined role vocabulary; that remapping is a project-level concern and belongs on a project-authored ADR whose `related-reqs` cites this REQ.",
6
+ "category": "functional",
7
+ "domain": "authentication",
8
+ "priority": "must",
9
+ "rationale": "Every project that uses Keycloak with role-based access must decide once whether its client is a per-client roles client or a realm-roles client; that decision is a Keycloak configuration and it lives on the realm record. Pushing role extraction to a single named adapter with a small surface keeps the project's handler code path free of Keycloak claim-shape knowledge and makes the role source observable in one place. Not remapping role strings inside the mechanism keeps the mechanism generic; a project that maps Keycloak roles onto its own project-role vocabulary does that once in an enrich-Principal hook the session-bridge invokes.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:06-roles"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-007",
3
+ "prdId": "PRD-001",
4
+ "title": "Refresh-token rotation follows the realm's configured rotation model",
5
+ "description": "Keycloak issues refresh tokens with a per-realm rotation configuration; the operator chooses at the realm layer whether refresh-token reuse is a hard fail (rotation with revoke-on-reuse) or a soft rotation (return a new refresh but let the old one live to expiry). The blueprint's refresh handler presents the current stored refresh token on every refresh call to the realm's `token_endpoint` with `grant_type=refresh_token`, replaces the stored value with the new one on a 2xx response, and treats a 4xx `invalid_grant` on refresh as a session-invalidation signal: the project session handle is invalidated server-side, the associated refresh-token chain is cleared, and one structured audit event `keycloak.refresh_invalidated` is emitted carrying the realm slug and the session-handle hash; no token value is emitted. The blueprint does NOT re-implement reuse-detection above the realm's own model: if the realm is configured to detect reuse and revoke, the realm's `invalid_grant` response IS the signal; if the realm is configured to allow soft rotation, that is the operator's declared risk posture and the blueprint honours it. This deliberately differs from the vendor-neutral sibling's stricter uniform posture; Keycloak owns the rotation model at the realm layer.",
6
+ "category": "functional",
7
+ "domain": "security",
8
+ "priority": "must",
9
+ "rationale": "The vendor-neutral sibling mandates rotation-with-reuse-detection because it cannot rely on the underlying provider to enforce it; Keycloak already enforces the operator's chosen model at the realm, so the blueprint's role is to honour the realm's decision rather than to layer a second policy on top. The operator's authority stops at the realm; overriding it in project code would fight the operator's own configuration.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:07-refresh"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-008",
3
+ "prdId": "PRD-001",
4
+ "title": "Session-vs-token separation: Keycloak owns tokens; the project may issue an app session on top",
5
+ "description": "Keycloak is the token authority: it mints the access token, the id_token, and the refresh token; the browser never holds a project-minted JWT and the browser never holds a Keycloak refresh token. The project MAY layer an application session on top of the verified token stream: the reference wiring issues an opaque-handle session cookie whose server-side record stores the Keycloak-minted tokens keyed by the handle (mirroring the vendor-neutral sibling's opaque-handle posture, and following the composition pattern where the session-record store injects). The alternate posture the operator MAY declare is `sessionMode: 'stateless'` on the realm record, in which case no server-side session record is kept and every request re-verifies the access token from a bearer header (or a cookie carrying the token verbatim, per the operator's declared bearer source). The two postures are not composable per realm record; the operator picks one. The reduced Principal (`principalId`, `provider`, `email?`, `roles[]`, `organisationIds[]`) is the only claim shape the project's own handlers ever see; provider tokens are inaccessible from `request.auth` in both modes.",
6
+ "category": "functional",
7
+ "domain": "authentication",
8
+ "priority": "must",
9
+ "rationale": "Keycloak's authority over token issuance is a vendor commitment; the blueprint's authority is over what the project builds around that stream. Two postures serve two operator populations: a stateful session-record wiring (the reference default) matches small projects that want a single opaque cookie and cheap sign-out, and a stateless bearer posture matches deployments where the project can accept per-request verification cost and wants to avoid a session store. Both postures gate provider tokens behind the mechanism boundary so no handler code path reads a Keycloak-minted token directly.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:08-session-vs-token"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-009",
3
+ "prdId": "PRD-001",
4
+ "title": "Local-first stand-up uses Keycloak's official container image; no realm data is committed",
5
+ "description": "The blueprint's local-first posture stands up a Keycloak realm from the vendor's official container image (`quay.io/keycloak/keycloak`; the operator pins the currently-supported stable tag at boot time and updates on the vendor's own upgrade cadence). The container is started in development mode with placeholder admin credentials the harness reads from a project environment file (never committed) and a realm-configuration seed the harness applies through the vendor's `kc.sh import` path or through the admin REST API. The seed carries a placeholder realm name (`example`), a placeholder client id, and placeholder role assignments; nothing that appears in the seed matches any real deployment. The reference asset under `assets/local-container/` names the shape a project's harness follows; the blueprint does not lift a specific compose file, a specific realm name, or a specific client id from any deployment. A project may substitute a persistent scratch service, a shared team-level container, or an alternate stand-up path (a system-installed Keycloak, a devcontainer, a Kubernetes namespace) as long as the resulting realm satisfies the discovery-endpoint shape and the client-configuration expectations REQ-002 and REQ-003 (or REQ-004) name.",
6
+ "category": "functional",
7
+ "domain": "verification",
8
+ "priority": "must",
9
+ "rationale": "Keycloak is self-hostable and the vendor publishes a container image designed for exactly this purpose; a project that wants runtime-verify against a real realm can stand one up per test suite and tear it down without paying for a cloud tenant. Committing to the official image (with an operator-chosen tag) keeps the mechanism upgradeable on the vendor's cadence rather than pinning a specific version in the blueprint. Placeholder-only seeds keep the blueprint's tree free of any deployment-shaped signal.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:09-local-stand-up"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-010",
3
+ "prdId": "PRD-001",
4
+ "title": "Sign-out invalidates the project session; Keycloak session-end is an operator-declared option",
5
+ "description": "A POST to the project's sign-out route invalidates the project session handle server-side and clears the session cookie (opaque-handle mode) or clears the bearer-source cookie (stateless mode); the next request from the browser is unauthenticated regardless of what the realm still holds. Provider-side sign-out through the realm's `end_session_endpoint` (RP-Initiated Logout, OIDC section) is NOT mandated: a realm record whose `providerLogout` field is `'redirect'` and whose `end_session_endpoint` is present on the discovery record causes the sign-out response to return a 302 to that endpoint after clearing the project session, with the `id_token_hint` query parameter carrying the raw id_token stored on the session record (opaque-handle mode) or the id_token the operator's declared bearer source has produced (stateless mode). A record that sets `providerLogout: 'redirect'` on a realm whose discovery omits `end_session_endpoint` is refused at boot with a stable-coded error naming the realm slug.",
6
+ "category": "functional",
7
+ "domain": "authentication",
8
+ "priority": "must",
9
+ "rationale": "The project-session invariant is `sign-out clears the project session`; RP-initiated logout is a legitimate but variable operator preference (some deployments want a full realm sign-out, others want the realm session to persist for the operator's other applications on the same realm). Declaring the posture on the realm record with a fail-fast on missing endpoint catches misconfiguration where the project believes it is signing out at the realm and silently is not.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:10-sign-out"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-011",
3
+ "prdId": "PRD-001",
4
+ "title": "Fail-safe boot when the realm is unreachable follows a declared posture",
5
+ "description": "A realm record declares a `bootPosture` field with three legal values: `strict` (default), `deferred`, and `standby`. Under `strict`, a discovery fetch failure at boot terminates the process with a stable-coded error naming the realm slug and the fetch failure class (`KEYCLOAK_DISCOVERY_UNREACHABLE`, `KEYCLOAK_DISCOVERY_MALFORMED`, `KEYCLOAK_DISCOVERY_TIMEOUT`); no listener is left running. Under `deferred`, the process starts, the affected realm is marked `unhealthy`, requests routed to that realm through the provider-router (REQ-005) refuse with `KEYCLOAK_REALM_UNAVAILABLE` and the discovery client (TAC-1201) attempts one exponential-backoff refresh loop bounded by `deferredRetryCapSeconds` (default 300); success populates the record and the realm goes `healthy`. Under `standby`, the process starts, the affected realm is marked `unhealthy` in the same way, and the discovery client stays quiet until a project-authored trigger (an admin route, a scheduled probe, an operator signal) calls `refresh(realmSlug)`; the operator opts in to standby only when the deployment demands manual control. A realm's health transitions are emitted as structured audit events `keycloak.realm_healthy` and `keycloak.realm_unhealthy` carrying the realm slug and the current class.",
6
+ "category": "functional",
7
+ "domain": "reliability",
8
+ "priority": "must",
9
+ "rationale": "A project deployed alongside a self-hosted realm inherits the realm's uptime characteristics. `strict` is the safest default for a small project (crash-loop until the realm is up, obvious to whoever wired the deployment); `deferred` matches deployments that want the process to survive a realm restart and self-heal; `standby` matches deployments where the operator wants to control when the process reaches the realm (for example, a migration window where a partial realm is not usable). Naming the posture on the record makes the choice a paper-trailed decision rather than a hidden retry constant.",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:11-fail-safe-boot"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "reqId": "security-auth-keycloak-REQ-012",
3
+ "prdId": "PRD-001",
4
+ "title": "Runtime verification binds against a local Keycloak container; cloud-tenant smokes are accountBound",
5
+ "description": "Runtime-verify ACs on this blueprint bind against a local Keycloak realm the project stands up per REQ-009 (or an equivalent locally-runnable realm the project already operates). The local realm is real Keycloak on the wire: discovery, JWKS, `token_endpoint`, `introspection_endpoint`, and `end_session_endpoint` all speak the standards-tracked shapes the verifier and the flow controller consume. ACs that require a smoke test against a project-owned cloud-hosted Keycloak deployment or a managed Keycloak service are `accountBound`: the runtime-verify runner marks them with an `accountBound` flag and a stable-coded skip reason (`KEYCLOAK_LIVE_REALM_UNREACHABLE`) when the project does not have live deployment credentials in the runtime environment; the CI pipeline does not fail on a skipped accountBound AC, and the ship gate reads the honest coverage. A project that has live credentials wires them and the accountBound smokes light up. The introspection-mode AC (US-11105) similarly binds against a local stub introspection responder shipped as a shape in `assets/mock-introspection/`; a project may substitute a second local Keycloak realm configured for opaque tokens or an equivalent implementation.",
6
+ "category": "functional",
7
+ "domain": "verification",
8
+ "priority": "must",
9
+ "rationale": "Keycloak is self-hostable; the local-container path is the honest runtime-verify surface and needs no external account. Cloud-hosted realm smokes are still valuable for a project that operates one but are not achievable in every CI environment, so the accountBound posture keeps CI honest without demanding a cloud realm every consumer maintains. The introspection stub covers the shape of the alternate verification mode without demanding a second Keycloak container per test suite (though a project may run one if it prefers a real endpoint).",
10
+ "tags": [
11
+ "blueprint:security-auth-keycloak",
12
+ "category:12-verify-posture"
13
+ ],
14
+ "version": "1.0.0",
15
+ "status": "approved",
16
+ "createdAt": "2026-08-31T00:00:00Z",
17
+ "updatedAt": "2026-08-31T00:00:00Z"
18
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "tacId": "TAC-1201-security-auth-keycloak-discovery-client",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Keycloak realm discovery client",
8
+ "purpose": "The module that fetches, validates, and caches the OIDC discovery document for every configured realm record. Owns the boot-time discovery fetch, the fail-safe posture per REQ-011, the refresh cadence, and the endpoint-projection onto the realm record the other TACs read. Does NOT own token verification and does NOT own request routing; those are TAC-1202/TAC-1203 and TAC-1204 respectively.",
9
+ "responsibilities": [
10
+ "Fetch `<issuerBaseUrl>/realms/<realm>/.well-known/openid-configuration` at boot for every realm record; parse the response; populate the record's endpoint fields from the response (AC-11102-1, AC-11102-2).",
11
+ "Refuse boot under `bootPosture: strict` on any fetch failure with a stable-coded error naming the realm slug and the failure class (`KEYCLOAK_DISCOVERY_UNREACHABLE`, `KEYCLOAK_DISCOVERY_MALFORMED`, `KEYCLOAK_DISCOVERY_TIMEOUT`) (AC-11111-1).",
12
+ "Under `bootPosture: deferred`, mark the affected realm `unhealthy`, run an exponential-backoff refresh loop bounded by `deferredRetryCapSeconds`, and flip to `healthy` on the first success (AC-11111-2).",
13
+ "Under `bootPosture: standby`, mark the affected realm `unhealthy` and expose a `refresh(realmSlug)` method the project's admin surface calls (AC-11111-3).",
14
+ "Verify that the discovery response carries the endpoints the record's `verificationMode` depends on: `jwks_uri` when mode is `jwks`, `introspection_endpoint` when mode is `introspection`; refuse boot with `KEYCLOAK_DISCOVERY_MISSING_ENDPOINT` on absence (AC-11102-3).",
15
+ "Verify that a record whose `providerLogout` is `'redirect'` maps to a discovery response that carries `end_session_endpoint`; refuse boot with `KEYCLOAK_LOGOUT_NOT_DECLARED` on absence (AC-11110-2, related to REQ-010).",
16
+ "Refresh the discovery document at the realm record's `discoveryRefreshSeconds` cadence (default 3600) in a background loop; a refresh failure logs a warning event but does not flip a healthy realm to unhealthy - the failure is transient by policy (AC-11102-2).",
17
+ "Emit structured audit events `keycloak.realm_healthy`, `keycloak.realm_unhealthy`, `keycloak.discovery_refreshed`, `keycloak.discovery_refused` carrying the realm slug; no field carries a client secret."
18
+ ],
19
+ "internalStructure": "One factory function producing a discovery-client closure over an injected `realmConfig` module (a plain data-record set the project maintains), an injected `httpFetch` port (the process HTTP client), an injected `clock`, an injected `auditSink`, and an injected `retryScheduler` used by the deferred posture. The internal cache is a Map keyed by realmSlug whose value is `{ endpoints, fetchedAt, health }`; refresh replaces atomically.",
20
+ "interfaces": [
21
+ {
22
+ "name": "realmConfig",
23
+ "kind": "read-api",
24
+ "description": "The project-authored realm-config module exposing `list() -> record[]`. Each record is a plain object with `{ realmSlug, issuerBaseUrl, realmName, clientId, clientSecret, verificationMode, roleClaimShape, scopes, redirectUri, providerLogout, sessionMode, bearerSource?, jwksCacheTtlSeconds?, introspectionCacheTtlSeconds?, bootPosture, deferredRetryCapSeconds?, discoveryRefreshSeconds? }`."
25
+ },
26
+ {
27
+ "name": "httpFetch",
28
+ "kind": "read-api",
29
+ "description": "Process HTTP-fetch port used for discovery pulls; test harness substitutes an in-process mock or points at a local Keycloak container."
30
+ },
31
+ {
32
+ "name": "clock",
33
+ "kind": "read-api",
34
+ "description": "Injected time source for TTL and refresh-cadence bookkeeping."
35
+ },
36
+ {
37
+ "name": "retryScheduler",
38
+ "kind": "callback",
39
+ "description": "Exponential-backoff scheduler used under bootPosture: deferred; caps the retry envelope at deferredRetryCapSeconds and reports success or exhaustion."
40
+ },
41
+ {
42
+ "name": "auditSink",
43
+ "kind": "callback",
44
+ "description": "Structured-event sink invoked with `{ event, realmSlug, class?, at }`; never invoked with a clientSecret or a token value."
45
+ }
46
+ ],
47
+ "dependencies": [],
48
+ "tradeoffs": "Fixing discovery at boot and refreshing on a fixed cadence trades a small piece of background work for the guarantee that the realm's URL layout can change under the process without a code edit. Three fail-safe postures let the operator pick their preferred crash-loop / self-heal / manual-control shape; the trade against a single hardcoded posture is one record field and a small policy branch. Emitting audit events on health transitions makes an unhealthy realm legible in the project's own log stream rather than only in the realm's operations surface.",
49
+ "createdAt": "2026-08-31T00:00:00Z",
50
+ "updatedAt": "2026-08-31T00:00:00Z"
51
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "tacId": "TAC-1202-security-auth-keycloak-jwt-verifier",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Keycloak JWT verifier",
8
+ "purpose": "The verifier the mechanism uses when the realm record's `verificationMode` is `jwks`. Owns the JWKS fetch cache with kid-miss refresh, the signature check, the standard-claims validation, and the extraction of the verified claim record the role adapter reads. Does NOT own the token stream (that is the flow controller in the sibling blueprints' vocabulary, delegated here to a project-side authorisation-code state machine that a project either authors above this blueprint or draws from the vendor-neutral sibling if composed).",
9
+ "responsibilities": [
10
+ "Fetch and cache the realm's JWKS by realmSlug; TTL is the record's `jwksCacheTtlSeconds` (default 900); refresh from the head of the cache queue at expiry (AC-11103-1).",
11
+ "Verify signature against a key resolved by `kid`; on a kid-miss trigger exactly one JWKS refresh; refuse the token with `KEYCLOAK_JWT_KID_UNKNOWN` when the refresh does not surface the kid (AC-11103-2).",
12
+ "Validate the standard claim set: `iss` equals the record's discovered issuer; `aud` contains the record's `clientId` OR `azp` equals it; `exp` in the future by at least one second; `nbf` not in the future; `iat` in the past by no more than `idTokenMaxAgeSeconds` (AC-11104-1).",
13
+ "Refuse with `KEYCLOAK_JWT_EXPIRED`, `KEYCLOAK_JWT_ISSUER_MISMATCH`, `KEYCLOAK_JWT_AUDIENCE_MISMATCH`, or `KEYCLOAK_JWT_SIGNATURE_INVALID` per failure class; emit one structured audit event `keycloak.jwt_refused` carrying realmSlug, reasonClass, and no token value (AC-11104-2).",
14
+ "Return the verified claim record `{ sub, email?, azp, iss, aud, exp, iat, resource_access?, realm_access?, plus other realm-declared claims }` to the caller; the caller (the role adapter, the session-bridge in opaque-handle mode, the bearer verifier in stateless mode) consumes from that record (AC-11101-3).",
15
+ "Refuse a JWKS fetch failure at verification time with `KEYCLOAK_JWKS_UNREACHABLE`; the token is refused, not silently trusted (AC-11103-2)."
16
+ ],
17
+ "internalStructure": "One factory function producing a verify closure over the injected discovery-client (TAC-1201, which exposes the record's discovered endpoints), an injected `httpFetch` port for JWKS, an injected clock, and an injected auditSink. The JWKS cache is a Map keyed by realmSlug whose value is `{ keys[], fetchedAt }`; the crypto is either the process's Web Crypto (Node 20+) or a project-injected primitive.",
18
+ "interfaces": [
19
+ {
20
+ "name": "discoveryClient",
21
+ "kind": "read-api",
22
+ "description": "TAC-1201's exported endpoint-record accessor: `endpoints(realmSlug) -> { issuer, jwksUri, ... }`."
23
+ },
24
+ {
25
+ "name": "httpFetch",
26
+ "kind": "read-api",
27
+ "description": "Process HTTP-fetch port; shared with the discovery client."
28
+ },
29
+ {
30
+ "name": "clock",
31
+ "kind": "read-api",
32
+ "description": "Injected time source for TTL and expiry checks."
33
+ },
34
+ {
35
+ "name": "auditSink",
36
+ "kind": "callback",
37
+ "description": "Structured-event sink; never invoked with a token value."
38
+ }
39
+ ],
40
+ "dependencies": [
41
+ { "name": "Discovery client", "kind": "tac", "tacId": "TAC-1201-security-auth-keycloak-discovery-client", "description": "Reads the realm's discovered endpoints." }
42
+ ],
43
+ "tradeoffs": "TTL-plus-kid-miss caching is the standard shape for JWKS verification; the trade against a shorter TTL is one background refresh per window; against a longer TTL is a bounded staleness window. Refusing on a kid-miss after one refresh defends against amplification while still recovering from a rotation the realm has published. Fixed refusal codes make failure classes observable in the audit stream without a runtime taxonomy the operator has to learn.",
44
+ "createdAt": "2026-08-31T00:00:00Z",
45
+ "updatedAt": "2026-08-31T00:00:00Z"
46
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "tacId": "TAC-1203-security-auth-keycloak-introspection-client",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Keycloak introspection client (RFC 7662)",
8
+ "purpose": "The verifier the mechanism uses when the realm record's `verificationMode` is `introspection`. Owns the RFC 7662 request shape (HTTP basic auth with `clientId:clientSecret`, `token` form parameter), the response handling, the optional short-lived cache, and the extraction of the verified claim record the role adapter reads.",
9
+ "responsibilities": [
10
+ "Issue a POST to the record's `introspection_endpoint` with `Content-Type: application/x-www-form-urlencoded`, `Authorization: Basic <base64(clientId:clientSecret)>`, and body `token=<the-access-token>` (AC-11105-1).",
11
+ "Parse the response body as JSON per RFC 7662; a response with `active: true` yields the claim record; a response with `active: false` refuses the token with `KEYCLOAK_INTROSPECTION_INACTIVE` (AC-11105-2).",
12
+ "Refuse a non-2xx response with `KEYCLOAK_INTROSPECTION_ENDPOINT_ERROR` naming the HTTP status class; emit `keycloak.introspection_refused` carrying realmSlug and status class (AC-11105-3).",
13
+ "Cache the response when the record's `introspectionCacheTtlSeconds` is greater than zero; cache key is a stable hash of the token value (SHA-256 hex prefix, never the token) so the cache does not carry token strings; default TTL is 0 (no cache); AC-11105-1 covers the uncached path.",
14
+ "Emit one structured audit event `keycloak.introspection_verified` on a 2xx `active: true` carrying realmSlug and no token value (AC-11105-1)."
15
+ ],
16
+ "internalStructure": "One factory function producing an introspect closure over the discovery client (for the endpoint), an injected `httpFetch`, an injected clock, and an injected auditSink. The optional cache is a Map keyed by the token-hash whose value is `{ claims, cachedAt }`; entries evict on TTL expiry from the read path.",
17
+ "interfaces": [
18
+ {
19
+ "name": "discoveryClient",
20
+ "kind": "read-api",
21
+ "description": "TAC-1201's exported endpoint-record accessor for `introspection_endpoint`."
22
+ },
23
+ {
24
+ "name": "httpFetch",
25
+ "kind": "read-api",
26
+ "description": "Process HTTP-fetch port."
27
+ },
28
+ {
29
+ "name": "clock",
30
+ "kind": "read-api",
31
+ "description": "Injected time source."
32
+ },
33
+ {
34
+ "name": "auditSink",
35
+ "kind": "callback",
36
+ "description": "Structured-event sink; never invoked with a token value."
37
+ }
38
+ ],
39
+ "dependencies": [
40
+ { "name": "Discovery client", "kind": "tac", "tacId": "TAC-1201-security-auth-keycloak-discovery-client", "description": "Provides the introspection endpoint from discovery." }
41
+ ],
42
+ "tradeoffs": "The introspection round-trip is expensive on the hot path; the optional cache lets an operator on a high-issuance realm claw back some of the cost at the price of a bounded revocation-reflection delay. Hashing the token as the cache key defends against a leaked cache dump: nothing on disk (or in a warmed memory dump) is a token; the hash cannot be exchanged. Refusing on non-2xx keeps the mechanism from falling back to a permissive posture when the endpoint is misbehaving.",
43
+ "createdAt": "2026-08-31T00:00:00Z",
44
+ "updatedAt": "2026-08-31T00:00:00Z"
45
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "tacId": "TAC-1204-security-auth-keycloak-provider-router",
3
+ "prdId": "PRD-001",
4
+ "tadId": "TAD-001",
5
+ "version": "1.0.0",
6
+ "status": "approved",
7
+ "name": "Provider router (issuer-selection seam)",
8
+ "purpose": "The named seam a project's own middleware invokes on every authenticated request to decide which realm record (or, at the operator's option, the sentinel `notThisIssuer` when the middleware layer above hands the decision off) is authoritative for the request. Does NOT own token verification and does NOT itself name any second issuer.",
9
+ "responsibilities": [
10
+ "Expose `route(request) -> { realmSlug, verificationMode } | notThisIssuer` as a pure function on the request shape; the function is injected by the project and the reference wiring provides a constant-realm implementation for the common single-realm deployment (AC-11106-1).",
11
+ "Refuse a routing decision that names a realmSlug not present in the realmConfig list with `KEYCLOAK_REALM_UNKNOWN`; the mechanism does not silently fall back to a default realm (AC-11106-2).",
12
+ "Honour the `notThisIssuer` sentinel by returning it verbatim to the caller (the project's own middleware chain); the mechanism does not decide what a caller does with the sentinel (AC-11106-3).",
13
+ "Emit one structured audit event per routing decision `keycloak.route_decided` carrying realmSlug (or the string `notThisIssuer`) and no request-content field."
14
+ ],
15
+ "internalStructure": "One factory function producing a router closure over an injected `routeFn` (the project-authored pure function) and an injected realmConfig list. The reference wiring is a constant-realm `routeFn` that returns the first configured realmSlug for every request; a multi-realm project supplies its own routeFn. The routeFn is the seam the ratified reshape ruling names in generic vocabulary; the blueprint refuses to inspect specific request features or to name any second issuer.",
16
+ "interfaces": [
17
+ {
18
+ "name": "routeFn",
19
+ "kind": "callback",
20
+ "description": "Pure function `(request) -> { realmSlug, verificationMode } | 'notThisIssuer'`. Project-authored; the blueprint's reference wiring provides a constant-realm default for single-realm projects."
21
+ },
22
+ {
23
+ "name": "realmConfig",
24
+ "kind": "read-api",
25
+ "description": "Same source TAC-1201 reads: the project-authored realm-config module."
26
+ },
27
+ {
28
+ "name": "auditSink",
29
+ "kind": "callback",
30
+ "description": "Structured-event sink; never carries request content."
31
+ }
32
+ ],
33
+ "dependencies": [],
34
+ "tradeoffs": "The seam is one function call and one config-record lookup on every request; the cost is negligible and the observability payoff scales with deployment complexity. Refusing to name a second issuer keeps the blueprint faithful to the ratified reshape ruling; a project that has one names it on a project-level ADR whose scope names the second issuer explicitly. Refusing an unknown realmSlug rather than falling back to a default matches the sibling's multi-provider-routing posture: no silent defaults.",
35
+ "createdAt": "2026-08-31T00:00:00Z",
36
+ "updatedAt": "2026-08-31T00:00:00Z"
37
+ }