command_tower 0.3.1 → 0.10.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 (603) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -28
  3. data/app/auth/command_tower/auth/auth_context.rb +36 -0
  4. data/app/auth/command_tower/auth/password_recovery_session_context.rb +11 -0
  5. data/app/auth/command_tower/auth/signup_session_context.rb +11 -0
  6. data/app/controllers/command_tower/admin/messaging/announcements_controller.rb +34 -0
  7. data/app/controllers/command_tower/application_controller.rb +80 -14
  8. data/app/controllers/command_tower/auth/email/availability_controller.rb +35 -0
  9. data/app/controllers/command_tower/auth/email_verification/send_controller.rb +24 -0
  10. data/app/controllers/command_tower/auth/email_verification/verify_controller.rb +41 -0
  11. data/app/controllers/command_tower/auth/identity_policy_controller.rb +14 -0
  12. data/app/controllers/command_tower/auth/logout_controller.rb +14 -0
  13. data/app/controllers/command_tower/auth/password_recovery_session/create_controller.rb +17 -0
  14. data/app/controllers/command_tower/auth/password_reset/reset_controller.rb +35 -0
  15. data/app/controllers/command_tower/auth/password_reset/send_controller.rb +40 -0
  16. data/app/controllers/command_tower/auth/password_reset/validate_controller.rb +35 -0
  17. data/app/controllers/command_tower/auth/plain_text/login_controller.rb +22 -0
  18. data/app/controllers/command_tower/auth/register_controller.rb +33 -0
  19. data/app/controllers/command_tower/auth/session_controller.rb +21 -0
  20. data/app/controllers/command_tower/auth/signup_session/create_controller.rb +17 -0
  21. data/app/controllers/command_tower/auth/username/availability_controller.rb +35 -0
  22. data/app/controllers/command_tower/me/inbox_controller.rb +85 -0
  23. data/app/controllers/command_tower/me/name_controller.rb +37 -0
  24. data/app/controllers/command_tower/me/password_controller.rb +37 -0
  25. data/app/controllers/command_tower/me/phone_controller.rb +44 -0
  26. data/app/controllers/command_tower/me/phone_verification/verifications_controller.rb +23 -0
  27. data/app/controllers/command_tower/me/phone_verification/verify_controller.rb +34 -0
  28. data/app/controllers/command_tower/me/preferences_controller.rb +46 -0
  29. data/app/controllers/command_tower/me/pushover/verifications_controller.rb +23 -0
  30. data/app/controllers/command_tower/me/pushover_controller.rb +66 -0
  31. data/app/controllers/command_tower/me_controller.rb +19 -0
  32. data/app/controllers/command_tower/profile_controller.rb +19 -0
  33. data/app/controllers/concerns/command_tower/api/application_response_renderer.rb +82 -0
  34. data/app/controllers/concerns/command_tower/api/deserializer_failure_details.rb +22 -0
  35. data/app/controllers/concerns/command_tower/auth/authentication_boundary.rb +34 -0
  36. data/app/controllers/concerns/command_tower/auth/authorization_boundary.rb +26 -0
  37. data/app/controllers/concerns/command_tower/auth/invalid_credentials_deserializer_renderer.rb +23 -0
  38. data/app/controllers/concerns/command_tower/auth/password_recovery_session_boundary.rb +29 -0
  39. data/app/controllers/concerns/command_tower/auth/signup_session_boundary.rb +29 -0
  40. data/app/deserializers/command_tower/deserializers/admin/messaging/create_announcement_deserializer.rb +106 -0
  41. data/app/deserializers/command_tower/deserializers/application_deserializer.rb +148 -0
  42. data/app/deserializers/command_tower/deserializers/auth/email_availability_deserializer.rb +19 -0
  43. data/app/deserializers/command_tower/deserializers/auth/email_verification/verify_deserializer.rb +21 -0
  44. data/app/deserializers/command_tower/deserializers/auth/password_reset/reset_deserializer.rb +31 -0
  45. data/app/deserializers/command_tower/deserializers/auth/password_reset/send_deserializer.rb +21 -0
  46. data/app/deserializers/command_tower/deserializers/auth/password_reset/validate_deserializer.rb +23 -0
  47. data/app/deserializers/command_tower/deserializers/auth/plain_text/login_deserializer.rb +24 -0
  48. data/app/deserializers/command_tower/deserializers/auth/register_deserializer.rb +42 -0
  49. data/app/deserializers/command_tower/deserializers/auth/username_availability_deserializer.rb +19 -0
  50. data/app/deserializers/command_tower/deserializers/clients/errors.rb +45 -0
  51. data/app/deserializers/command_tower/deserializers/clients/missing.rb +10 -0
  52. data/app/deserializers/command_tower/deserializers/clients/payload.rb +21 -0
  53. data/app/deserializers/command_tower/deserializers/clients/result.rb +122 -0
  54. data/app/deserializers/command_tower/deserializers/clients/types.rb +192 -0
  55. data/app/deserializers/command_tower/deserializers/coercion_result.rb +35 -0
  56. data/app/deserializers/command_tower/deserializers/failure.rb +14 -0
  57. data/app/deserializers/command_tower/deserializers/me/change_password_deserializer.rb +41 -0
  58. data/app/deserializers/command_tower/deserializers/me/phone_verification/verify_deserializer.rb +29 -0
  59. data/app/deserializers/command_tower/deserializers/me/pushover/credentials_deserializer.rb +31 -0
  60. data/app/deserializers/command_tower/deserializers/me/update_name_deserializer.rb +22 -0
  61. data/app/deserializers/command_tower/deserializers/me/update_phone_deserializer.rb +22 -0
  62. data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +77 -0
  63. data/app/deserializers/command_tower/deserializers/messaging/preferences/show_deserializer.rb +17 -0
  64. data/app/deserializers/command_tower/deserializers/messaging/preferences/update_deserializer.rb +97 -0
  65. data/app/errors/command_tower/errors/account/phone_already_verified_error.rb +17 -0
  66. data/app/errors/command_tower/errors/account/phone_missing_error.rb +17 -0
  67. data/app/errors/command_tower/errors/account/phone_verification_code_invalid_error.rb +17 -0
  68. data/app/errors/command_tower/errors/account/phone_verification_expired_error.rb +17 -0
  69. data/app/errors/command_tower/errors/account/phone_verification_send_failed_error.rb +25 -0
  70. data/app/errors/command_tower/errors/account/phone_verification_stale_error.rb +17 -0
  71. data/app/errors/command_tower/errors/account/phone_verification_throttled_error.rb +24 -0
  72. data/app/errors/command_tower/errors/account/pushover_already_configured_error.rb +21 -0
  73. data/app/errors/command_tower/errors/account/pushover_capability_unavailable_error.rb +21 -0
  74. data/app/errors/command_tower/errors/account/pushover_not_configured_error.rb +21 -0
  75. data/app/errors/command_tower/errors/account/pushover_provider_unavailable_error.rb +21 -0
  76. data/app/errors/command_tower/errors/account/pushover_verification_failed_error.rb +27 -0
  77. data/app/errors/command_tower/errors/account/sms_capability_unavailable_error.rb +21 -0
  78. data/app/errors/command_tower/errors/application_error.rb +27 -0
  79. data/app/errors/command_tower/errors/auth/csrf_mismatch_error.rb +17 -0
  80. data/app/errors/command_tower/errors/auth/csrf_missing_error.rb +17 -0
  81. data/app/errors/command_tower/errors/auth/email_already_registered_error.rb +17 -0
  82. data/app/errors/command_tower/errors/auth/email_verification_required_error.rb +21 -0
  83. data/app/errors/command_tower/errors/auth/invalid_credentials_error.rb +21 -0
  84. data/app/errors/command_tower/errors/auth/password_recovery_ip_rate_limit_error.rb +25 -0
  85. data/app/errors/command_tower/errors/auth/password_recovery_session_expired_error.rb +21 -0
  86. data/app/errors/command_tower/errors/auth/password_recovery_session_invalid_error.rb +21 -0
  87. data/app/errors/command_tower/errors/auth/password_recovery_session_missing_error.rb +21 -0
  88. data/app/errors/command_tower/errors/auth/password_recovery_session_rate_limit_error.rb +25 -0
  89. data/app/errors/command_tower/errors/auth/password_reset_invalid_token_error.rb +21 -0
  90. data/app/errors/command_tower/errors/auth/password_reset_unavailable_error.rb +21 -0
  91. data/app/errors/command_tower/errors/auth/signup_ip_rate_limit_error.rb +25 -0
  92. data/app/errors/command_tower/errors/auth/signup_session_expired_error.rb +17 -0
  93. data/app/errors/command_tower/errors/auth/signup_session_invalid_error.rb +17 -0
  94. data/app/errors/command_tower/errors/auth/signup_session_missing_error.rb +17 -0
  95. data/app/errors/command_tower/errors/auth/signup_session_rate_limit_error.rb +25 -0
  96. data/app/errors/command_tower/errors/auth/verification_code_invalid_error.rb +17 -0
  97. data/app/errors/command_tower/errors/auth/verification_send_failed_error.rb +25 -0
  98. data/app/errors/command_tower/errors/forbidden_error.rb +19 -0
  99. data/app/errors/command_tower/errors/internal_error.rb +19 -0
  100. data/app/errors/command_tower/errors/messaging/accept_rejected_error.rb +21 -0
  101. data/app/errors/command_tower/errors/messaging/idempotency_conflict_error.rb +21 -0
  102. data/app/errors/command_tower/errors/messaging/recipient_unresolved_error.rb +21 -0
  103. data/app/errors/command_tower/errors/not_found_error.rb +19 -0
  104. data/app/errors/command_tower/errors/unauthorized_error.rb +19 -0
  105. data/app/errors/command_tower/errors/validation_error.rb +15 -0
  106. data/app/jobs/command_tower/messaging/channel_delivery_execution_job.rb +18 -0
  107. data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +24 -0
  108. data/app/jobs/command_tower/messaging/execution_recovery_job.rb +13 -0
  109. data/app/jobs/command_tower/messaging/handoff_job.rb +15 -0
  110. data/app/jobs/command_tower/messaging/handoff_recovery_job.rb +13 -0
  111. data/app/mailers/command_tower/email_verification_mailer.rb +5 -3
  112. data/app/mailers/command_tower/messaging/channel_mailer.rb +25 -0
  113. data/app/mailers/command_tower/password_reset_mailer.rb +18 -0
  114. data/app/models/command_tower/messaging/channel_delivery.rb +45 -0
  115. data/app/models/command_tower/messaging/communication.rb +60 -0
  116. data/app/models/command_tower/messaging/delivery_attempt.rb +28 -0
  117. data/app/models/command_tower/messaging/destination_plan.rb +17 -0
  118. data/app/models/command_tower/messaging/endpoint.rb +98 -0
  119. data/app/models/command_tower/messaging/endpoint_pushover_credential.rb +18 -0
  120. data/app/models/command_tower/messaging/inbox_item.rb +52 -0
  121. data/app/models/command_tower/messaging/notification_preference.rb +21 -0
  122. data/app/models/user.rb +4 -1
  123. data/app/serializers/command_tower/serializers/admin/messaging/announcement_response_serializer.rb +41 -0
  124. data/app/serializers/command_tower/serializers/application/envelope_serializer.rb +25 -0
  125. data/app/serializers/command_tower/serializers/application/error_entry_serializer.rb +18 -0
  126. data/app/serializers/command_tower/serializers/application_serializer.rb +26 -0
  127. data/app/serializers/command_tower/serializers/auth/email_availability_serializer.rb +17 -0
  128. data/app/serializers/command_tower/serializers/auth/email_verification/message_response_serializer.rb +15 -0
  129. data/app/serializers/command_tower/serializers/auth/identity_policy_serializer.rb +36 -0
  130. data/app/serializers/command_tower/serializers/auth/login_response_serializer.rb +17 -0
  131. data/app/serializers/command_tower/serializers/auth/logout_response_serializer.rb +13 -0
  132. data/app/serializers/command_tower/serializers/auth/password_recovery_session_response_serializer.rb +16 -0
  133. data/app/serializers/command_tower/serializers/auth/password_reset/message_response_serializer.rb +15 -0
  134. data/app/serializers/command_tower/serializers/auth/password_reset/validate_response_serializer.rb +22 -0
  135. data/app/serializers/command_tower/serializers/auth/register_response_serializer.rb +16 -0
  136. data/app/serializers/command_tower/serializers/auth/session_response_serializer.rb +16 -0
  137. data/app/serializers/command_tower/serializers/auth/signup_session_response_serializer.rb +16 -0
  138. data/app/serializers/command_tower/serializers/auth/user_serializer.rb +21 -0
  139. data/app/serializers/command_tower/serializers/auth/username_availability_serializer.rb +17 -0
  140. data/app/serializers/command_tower/serializers/me/account_serializer.rb +45 -0
  141. data/app/serializers/command_tower/serializers/me/change_password_response_serializer.rb +13 -0
  142. data/app/serializers/command_tower/serializers/me/pushover_serializer.rb +59 -0
  143. data/app/serializers/command_tower/serializers/messaging/inbox.rb +45 -0
  144. data/app/serializers/command_tower/serializers/messaging/preferences/catalog_serializer.rb +17 -0
  145. data/app/serializers/command_tower/serializers/messaging/preferences/category_serializer.rb +23 -0
  146. data/app/serializers/command_tower/serializers/messaging/preferences/notification_serializer.rb +30 -0
  147. data/app/serializers/command_tower/serializers/profile/profile_serializer.rb +13 -0
  148. data/app/services/command_tower/README.md +22 -6
  149. data/app/services/command_tower/auth/request_context.rb +21 -0
  150. data/app/services/command_tower/authorize/validate.rb +54 -18
  151. data/app/services/command_tower/clients/client_base.rb +183 -0
  152. data/app/services/command_tower/clients/client_result.rb +45 -0
  153. data/app/services/command_tower/clients/const_resolution.rb +32 -0
  154. data/app/services/command_tower/clients/decoded_response.rb +16 -0
  155. data/app/services/command_tower/clients/endpoint_base.rb +242 -0
  156. data/app/services/command_tower/clients/endpoint_input.rb +18 -0
  157. data/app/services/command_tower/clients/errors/authentication_error.rb +26 -0
  158. data/app/services/command_tower/clients/errors/configuration_error.rb +10 -0
  159. data/app/services/command_tower/clients/errors/deserialization_error.rb +26 -0
  160. data/app/services/command_tower/clients/errors/discovery_error.rb +10 -0
  161. data/app/services/command_tower/clients/errors/upstream_error.rb +26 -0
  162. data/app/services/command_tower/clients/invocation.rb +61 -0
  163. data/app/services/command_tower/clients/namespace_proxy.rb +75 -0
  164. data/app/services/command_tower/clients/scoped_client.rb +88 -0
  165. data/app/services/command_tower/clients/serialized_request.rb +17 -0
  166. data/app/services/command_tower/clients/transport/error.rb +18 -0
  167. data/app/services/command_tower/clients/transport/faraday_adapter.rb +129 -0
  168. data/app/services/command_tower/clients/transport/faraday_config.rb +45 -0
  169. data/app/services/command_tower/clients/transport/request.rb +50 -0
  170. data/app/services/command_tower/clients/transport/response.rb +22 -0
  171. data/app/services/command_tower/clients/url.rb +56 -0
  172. data/app/services/command_tower/identity/phone/normalizer.rb +41 -0
  173. data/app/services/command_tower/identity/phone_verification/sms_configuration.rb +58 -0
  174. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/fake_adapter.rb +35 -0
  175. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/log_adapter.rb +34 -0
  176. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/twilio_adapter.rb +60 -0
  177. data/app/services/command_tower/identity/phone_verification/sms_transport.rb +42 -0
  178. data/app/services/command_tower/jwt/authenticate_user.rb +61 -34
  179. data/app/services/command_tower/jwt/authentication_outcome.rb +36 -0
  180. data/app/services/command_tower/jwt/decode.rb +27 -8
  181. data/app/services/command_tower/jwt/encode.rb +4 -6
  182. data/app/services/command_tower/jwt/login_create.rb +6 -7
  183. data/app/services/command_tower/login_strategy/plain_text/email_verification/required.rb +11 -9
  184. data/app/services/command_tower/messaging/accept/channel_delivery_result.rb +13 -0
  185. data/app/services/command_tower/messaging/accept/coordinator.rb +166 -0
  186. data/app/services/command_tower/messaging/accept/error.rb +9 -0
  187. data/app/services/command_tower/messaging/accept/excluded_destination.rb +24 -0
  188. data/app/services/command_tower/messaging/accept/handoff_scheduler.rb +40 -0
  189. data/app/services/command_tower/messaging/accept/idempotency_conflict_error.rb +9 -0
  190. data/app/services/command_tower/messaging/accept/illegal_override_error.rb +9 -0
  191. data/app/services/command_tower/messaging/accept/impossible_mandatory_plan_error.rb +9 -0
  192. data/app/services/command_tower/messaging/accept/invalid_preference_error.rb +9 -0
  193. data/app/services/command_tower/messaging/accept/invariant_error.rb +9 -0
  194. data/app/services/command_tower/messaging/accept/operation_logger.rb +97 -0
  195. data/app/services/command_tower/messaging/accept/persistence_error.rb +9 -0
  196. data/app/services/command_tower/messaging/accept/persister.rb +90 -0
  197. data/app/services/command_tower/messaging/accept/request_normalizer.rb +100 -0
  198. data/app/services/command_tower/messaging/accept/result.rb +99 -0
  199. data/app/services/command_tower/messaging/accept/unknown_type_error.rb +9 -0
  200. data/app/services/command_tower/messaging/accept/validation_error.rb +9 -0
  201. data/app/services/command_tower/messaging/channel_detectors.rb +52 -0
  202. data/app/services/command_tower/messaging/channels/definition.rb +24 -0
  203. data/app/services/command_tower/messaging/channels/error.rb +9 -0
  204. data/app/services/command_tower/messaging/channels/unknown_channel_error.rb +9 -0
  205. data/app/services/command_tower/messaging/channels.rb +85 -0
  206. data/app/services/command_tower/messaging/contract/communications.rb +29 -0
  207. data/app/services/command_tower/messaging/contract/error.rb +9 -0
  208. data/app/services/command_tower/messaging/contract/internal/finder.rb +43 -0
  209. data/app/services/command_tower/messaging/contract/invariant_error.rb +9 -0
  210. data/app/services/command_tower/messaging/contract/mappers/channel_delivery_mapper.rb +53 -0
  211. data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +61 -0
  212. data/app/services/command_tower/messaging/contract/mappers/delivery_attempt_mapper.rb +27 -0
  213. data/app/services/command_tower/messaging/contract/mappers/destination_plan_mapper.rb +17 -0
  214. data/app/services/command_tower/messaging/contract/mappers/inbox_item_mapper.rb +17 -0
  215. data/app/services/command_tower/messaging/contract/not_found_error.rb +9 -0
  216. data/app/services/command_tower/messaging/contract/observability/correlation.rb +17 -0
  217. data/app/services/command_tower/messaging/contract/observability/operation_logger.rb +101 -0
  218. data/app/services/command_tower/messaging/contract/observability/structured_logger.rb +56 -0
  219. data/app/services/command_tower/messaging/contract/requests/find_communication.rb +21 -0
  220. data/app/services/command_tower/messaging/contract/results/channel_delivery_result.rb +25 -0
  221. data/app/services/command_tower/messaging/contract/results/communication_result.rb +22 -0
  222. data/app/services/command_tower/messaging/contract/results/delivery_attempt_result.rb +21 -0
  223. data/app/services/command_tower/messaging/contract/results/destination_plan_result.rb +11 -0
  224. data/app/services/command_tower/messaging/contract/results/inbox_item_result.rb +11 -0
  225. data/app/services/command_tower/messaging/contract/validation_error.rb +9 -0
  226. data/app/services/command_tower/messaging/delivery_retry_policy.rb +30 -0
  227. data/app/services/command_tower/messaging/endpoints/begin_verification.rb +13 -0
  228. data/app/services/command_tower/messaging/endpoints/channel_gate.rb +39 -0
  229. data/app/services/command_tower/messaging/endpoints/conflict_error.rb +9 -0
  230. data/app/services/command_tower/messaging/endpoints/create.rb +181 -0
  231. data/app/services/command_tower/messaging/endpoints/error.rb +9 -0
  232. data/app/services/command_tower/messaging/endpoints/fingerprinter.rb +50 -0
  233. data/app/services/command_tower/messaging/endpoints/invalid_transition_error.rb +9 -0
  234. data/app/services/command_tower/messaging/endpoints/lifecycle.rb +37 -0
  235. data/app/services/command_tower/messaging/endpoints/mark_invalid.rb +32 -0
  236. data/app/services/command_tower/messaging/endpoints/mark_verification_failed.rb +13 -0
  237. data/app/services/command_tower/messaging/endpoints/mark_verified.rb +13 -0
  238. data/app/services/command_tower/messaging/endpoints/not_found_error.rb +9 -0
  239. data/app/services/command_tower/messaging/endpoints/persistence.rb +91 -0
  240. data/app/services/command_tower/messaging/endpoints/pushover/verify.rb +101 -0
  241. data/app/services/command_tower/messaging/endpoints/query.rb +28 -0
  242. data/app/services/command_tower/messaging/endpoints/replace.rb +181 -0
  243. data/app/services/command_tower/messaging/endpoints/reset_verification.rb +13 -0
  244. data/app/services/command_tower/messaging/endpoints/revoke.rb +40 -0
  245. data/app/services/command_tower/messaging/endpoints/safe_view.rb +50 -0
  246. data/app/services/command_tower/messaging/endpoints/secret_box.rb +86 -0
  247. data/app/services/command_tower/messaging/endpoints/secret_reader.rb +72 -0
  248. data/app/services/command_tower/messaging/endpoints/validation_error.rb +9 -0
  249. data/app/services/command_tower/messaging/endpoints/validators/push.rb +24 -0
  250. data/app/services/command_tower/messaging/endpoints/validators/pushover.rb +68 -0
  251. data/app/services/command_tower/messaging/endpoints/validators.rb +36 -0
  252. data/app/services/command_tower/messaging/endpoints/verification.rb +39 -0
  253. data/app/services/command_tower/messaging/endpoints/verification_failed_error.rb +18 -0
  254. data/app/services/command_tower/messaging/endpoints/verification_transition.rb +20 -0
  255. data/app/services/command_tower/messaging/endpoints.rb +55 -0
  256. data/app/services/command_tower/messaging/execution/adapter_request.rb +45 -0
  257. data/app/services/command_tower/messaging/execution/adapter_result.rb +81 -0
  258. data/app/services/command_tower/messaging/execution/adapters/email/adapter.rb +78 -0
  259. data/app/services/command_tower/messaging/execution/adapters/email/configuration.rb +55 -0
  260. data/app/services/command_tower/messaging/execution/adapters/fake_adapter.rb +34 -0
  261. data/app/services/command_tower/messaging/execution/adapters/pushover/adapter.rb +107 -0
  262. data/app/services/command_tower/messaging/execution/adapters/pushover/configuration.rb +30 -0
  263. data/app/services/command_tower/messaging/execution/adapters/sms/adapter.rb +90 -0
  264. data/app/services/command_tower/messaging/execution/adapters/sms/configuration.rb +58 -0
  265. data/app/services/command_tower/messaging/execution/adapters/sms/log_adapter.rb +34 -0
  266. data/app/services/command_tower/messaging/execution/adapters/sms/twilio_http_client.rb +70 -0
  267. data/app/services/command_tower/messaging/execution/adapters/unconfigured_adapter.rb +20 -0
  268. data/app/services/command_tower/messaging/execution/attempt_ledger.rb +146 -0
  269. data/app/services/command_tower/messaging/execution/claim.rb +35 -0
  270. data/app/services/command_tower/messaging/execution/invalid_adapter_contract_error.rb +9 -0
  271. data/app/services/command_tower/messaging/execution/operation_logger.rb +237 -0
  272. data/app/services/command_tower/messaging/execution/readiness_terminal_mapping.rb +37 -0
  273. data/app/services/command_tower/messaging/execution/recovery.rb +78 -0
  274. data/app/services/command_tower/messaging/execution/resolve_recipient_address.rb +58 -0
  275. data/app/services/command_tower/messaging/execution/revalidate_readiness.rb +73 -0
  276. data/app/services/command_tower/messaging/execution/scheduler.rb +38 -0
  277. data/app/services/command_tower/messaging/execution/select_adapter.rb +63 -0
  278. data/app/services/command_tower/messaging/handoff/advance_communication.rb +39 -0
  279. data/app/services/command_tower/messaging/handoff/operation_logger.rb +78 -0
  280. data/app/services/command_tower/messaging/handoff/recovery.rb +26 -0
  281. data/app/services/command_tower/messaging/inbox/bulk_result.rb +19 -0
  282. data/app/services/command_tower/messaging/inbox/error.rb +9 -0
  283. data/app/services/command_tower/messaging/inbox/invalid_bulk_items_error.rb +19 -0
  284. data/app/services/command_tower/messaging/inbox/invariant_error.rb +9 -0
  285. data/app/services/command_tower/messaging/inbox/item_result.rb +64 -0
  286. data/app/services/command_tower/messaging/inbox/list_result.rb +18 -0
  287. data/app/services/command_tower/messaging/inbox/mutator.rb +252 -0
  288. data/app/services/command_tower/messaging/inbox/not_found_error.rb +9 -0
  289. data/app/services/command_tower/messaging/inbox/operation_logger.rb +37 -0
  290. data/app/services/command_tower/messaging/inbox/reader.rb +117 -0
  291. data/app/services/command_tower/messaging/inbox/scope.rb +24 -0
  292. data/app/services/command_tower/messaging/inbox/unread_count_result.rb +16 -0
  293. data/app/services/command_tower/messaging/inbox/validation_error.rb +9 -0
  294. data/app/services/command_tower/messaging/inbox.rb +61 -0
  295. data/app/services/command_tower/messaging/notification_types/catalog_category.rb +19 -0
  296. data/app/services/command_tower/messaging/notification_types/declaration.rb +96 -0
  297. data/app/services/command_tower/messaging/notification_types/declaration_validator.rb +152 -0
  298. data/app/services/command_tower/messaging/notification_types/duplicate_type_error.rb +9 -0
  299. data/app/services/command_tower/messaging/notification_types/error.rb +9 -0
  300. data/app/services/command_tower/messaging/notification_types/invalid_declaration_error.rb +9 -0
  301. data/app/services/command_tower/messaging/notification_types/not_found_error.rb +9 -0
  302. data/app/services/command_tower/messaging/notification_types/registry.rb +103 -0
  303. data/app/services/command_tower/messaging/notification_types/sealed_registry_error.rb +9 -0
  304. data/app/services/command_tower/messaging/notification_types.rb +37 -0
  305. data/app/services/command_tower/messaging/planner/destination_plan.rb +41 -0
  306. data/app/services/command_tower/messaging/planner/error.rb +9 -0
  307. data/app/services/command_tower/messaging/planner/excluded_destination.rb +28 -0
  308. data/app/services/command_tower/messaging/planner/illegal_override_error.rb +9 -0
  309. data/app/services/command_tower/messaging/planner/impossible_mandatory_plan_error.rb +9 -0
  310. data/app/services/command_tower/messaging/planner/invalid_evaluation_error.rb +9 -0
  311. data/app/services/command_tower/messaging/planner/message_overrides.rb +33 -0
  312. data/app/services/command_tower/messaging/planner/operation_logger.rb +28 -0
  313. data/app/services/command_tower/messaging/planner/planner.rb +261 -0
  314. data/app/services/command_tower/messaging/planner/unknown_type_error.rb +9 -0
  315. data/app/services/command_tower/messaging/planner.rb +25 -0
  316. data/app/services/command_tower/messaging/preferences/catalog.rb +96 -0
  317. data/app/services/command_tower/messaging/preferences/catalog_category_result.rb +19 -0
  318. data/app/services/command_tower/messaging/preferences/catalog_notification_result.rb +55 -0
  319. data/app/services/command_tower/messaging/preferences/catalog_result.rb +13 -0
  320. data/app/services/command_tower/messaging/preferences/error.rb +9 -0
  321. data/app/services/command_tower/messaging/preferences/evaluation_result.rb +61 -0
  322. data/app/services/command_tower/messaging/preferences/evaluator.rb +194 -0
  323. data/app/services/command_tower/messaging/preferences/invalid_preference_state_error.rb +9 -0
  324. data/app/services/command_tower/messaging/preferences/invalid_preference_write_error.rb +10 -0
  325. data/app/services/command_tower/messaging/preferences/not_settings_visible_error.rb +10 -0
  326. data/app/services/command_tower/messaging/preferences/preference_state.rb +61 -0
  327. data/app/services/command_tower/messaging/preferences/reason_classes.rb +17 -0
  328. data/app/services/command_tower/messaging/preferences/resolve.rb +50 -0
  329. data/app/services/command_tower/messaging/preferences/store.rb +126 -0
  330. data/app/services/command_tower/messaging/preferences/store_error.rb +10 -0
  331. data/app/services/command_tower/messaging/preferences/suppressed_destination.rb +24 -0
  332. data/app/services/command_tower/messaging/preferences/unknown_type_error.rb +9 -0
  333. data/app/services/command_tower/messaging/preferences/update.rb +248 -0
  334. data/app/services/command_tower/messaging/preferences.rb +68 -0
  335. data/app/services/command_tower/messaging/pushover/adapters/disabled_adapter.rb +33 -0
  336. data/app/services/command_tower/messaging/pushover/adapters/fake_adapter.rb +90 -0
  337. data/app/services/command_tower/messaging/pushover/adapters/http_adapter.rb +145 -0
  338. data/app/services/command_tower/messaging/pushover/adapters/log_adapter.rb +52 -0
  339. data/app/services/command_tower/messaging/pushover/result.rb +34 -0
  340. data/app/services/command_tower/messaging/pushover/transport.rb +64 -0
  341. data/app/services/command_tower/messaging/recipient_readiness/channel_result.rb +55 -0
  342. data/app/services/command_tower/messaging/recipient_readiness/error.rb +9 -0
  343. data/app/services/command_tower/messaging/recipient_readiness/evaluate.rb +319 -0
  344. data/app/services/command_tower/messaging/recipient_readiness/identity_facts.rb +54 -0
  345. data/app/services/command_tower/messaging/recipient_readiness/reason_codes.rb +33 -0
  346. data/app/services/command_tower/messaging/recipient_readiness/recipient_not_found_error.rb +9 -0
  347. data/app/services/command_tower/messaging/recipient_readiness/recipient_result.rb +38 -0
  348. data/app/services/command_tower/messaging/recipient_readiness/unknown_channel_error.rb +9 -0
  349. data/app/services/command_tower/messaging/recipient_readiness.rb +26 -0
  350. data/app/services/command_tower/messaging/rendering/channel_renderer.rb +127 -0
  351. data/app/services/command_tower/messaging/rendering/render_error.rb +24 -0
  352. data/app/services/command_tower/messaging/rendering/rendered_payload.rb +44 -0
  353. data/app/services/command_tower/messaging/rendering/rendered_pushover_payload.rb +43 -0
  354. data/app/services/command_tower/messaging/rendering/rendered_sms_payload.rb +40 -0
  355. data/app/services/command_tower/messaging.rb +35 -0
  356. data/app/services/command_tower/secrets/cleanse.rb +19 -5
  357. data/app/services/command_tower/secrets/generate.rb +62 -27
  358. data/app/services/command_tower/secrets/verify.rb +46 -12
  359. data/app/services/command_tower/secrets.rb +2 -0
  360. data/app/services/command_tower/services/account/clear_phone.rb +25 -0
  361. data/app/services/command_tower/services/account/phone_verification/send.rb +114 -0
  362. data/app/services/command_tower/services/account/phone_verification/verify.rb +88 -0
  363. data/app/services/command_tower/services/account/pushover/create.rb +28 -0
  364. data/app/services/command_tower/services/account/pushover/ct_support.rb +70 -0
  365. data/app/services/command_tower/services/account/pushover/destroy.rb +30 -0
  366. data/app/services/command_tower/services/account/pushover/replace.rb +28 -0
  367. data/app/services/command_tower/services/account/pushover/show.rb +19 -0
  368. data/app/services/command_tower/services/account/pushover/verify.rb +30 -0
  369. data/app/services/command_tower/services/account/update_phone.rb +50 -0
  370. data/app/services/command_tower/services/application_service.rb +21 -0
  371. data/app/services/command_tower/services/auth/authenticate_session.rb +105 -0
  372. data/app/services/command_tower/services/auth/authorize_request.rb +34 -0
  373. data/app/services/command_tower/services/auth/client_ip_resolver.rb +13 -0
  374. data/app/services/command_tower/services/auth/email_availability.rb +42 -0
  375. data/app/services/command_tower/services/auth/email_verification/send.rb +65 -0
  376. data/app/services/command_tower/services/auth/email_verification/verify.rb +51 -0
  377. data/app/services/command_tower/services/auth/identity_policy.rb +60 -0
  378. data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_send.rb +48 -0
  379. data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_token_issue.rb +35 -0
  380. data/app/services/command_tower/services/auth/password_recovery/rate_limits/enforcement.rb +53 -0
  381. data/app/services/command_tower/services/auth/password_recovery_session/create.rb +26 -0
  382. data/app/services/command_tower/services/auth/password_recovery_session/decode.rb +29 -0
  383. data/app/services/command_tower/services/auth/password_recovery_session/encode.rb +31 -0
  384. data/app/services/command_tower/services/auth/password_recovery_session/validate.rb +58 -0
  385. data/app/services/command_tower/services/auth/password_reset/reset.rb +65 -0
  386. data/app/services/command_tower/services/auth/password_reset/send.rb +77 -0
  387. data/app/services/command_tower/services/auth/password_reset/token_verification.rb +68 -0
  388. data/app/services/command_tower/services/auth/password_reset/validate.rb +29 -0
  389. data/app/services/command_tower/services/auth/plain_text/login.rb +44 -0
  390. data/app/services/command_tower/services/auth/register.rb +77 -0
  391. data/app/services/command_tower/services/auth/signup_rate_limits/check_availability.rb +52 -0
  392. data/app/services/command_tower/services/auth/signup_rate_limits/check_register.rb +24 -0
  393. data/app/services/command_tower/services/auth/signup_rate_limits/check_token_issue.rb +33 -0
  394. data/app/services/command_tower/services/auth/signup_rate_limits/enforcement.rb +50 -0
  395. data/app/services/command_tower/services/auth/signup_session/create.rb +26 -0
  396. data/app/services/command_tower/services/auth/signup_session/decode.rb +29 -0
  397. data/app/services/command_tower/services/auth/signup_session/encode.rb +31 -0
  398. data/app/services/command_tower/services/auth/signup_session/validate.rb +58 -0
  399. data/app/services/command_tower/services/auth/username_availability.rb +28 -0
  400. data/app/services/command_tower/services/me/capabilities.rb +43 -0
  401. data/app/services/command_tower/services/me/change_password.rb +84 -0
  402. data/app/services/command_tower/services/me/pushover_product_gate.rb +18 -0
  403. data/app/services/command_tower/services/me/sms_product_gate.rb +19 -0
  404. data/app/services/command_tower/services/me/update_name.rb +58 -0
  405. data/app/services/command_tower/services/messaging/communications/produce.rb +68 -0
  406. data/app/services/command_tower/services/messaging/communications/produce_many.rb +154 -0
  407. data/app/services/command_tower/services/messaging/inbox.rb +129 -0
  408. data/app/services/command_tower/services/messaging/preferences/platform_enabled_channels.rb +20 -0
  409. data/app/services/command_tower/services/messaging/preferences/show.rb +36 -0
  410. data/app/services/command_tower/services/messaging/preferences/update.rb +43 -0
  411. data/app/services/command_tower/services/messaging/recipients.rb +20 -0
  412. data/app/services/command_tower/services/rate_limits/check.rb +31 -0
  413. data/app/services/command_tower/services/service_result.rb +73 -0
  414. data/app/services/command_tower/user_attributes/mutate.rb +154 -0
  415. data/app/services/command_tower/username/available.rb +24 -11
  416. data/app/views/command_tower/email_verification_mailer/verify_email.html.erb +70 -22
  417. data/app/views/command_tower/messaging/rendering/email.html.erb +28 -0
  418. data/app/views/command_tower/messaging/rendering/email.text.erb +7 -0
  419. data/app/views/command_tower/messaging/rendering/pushover.text.erb +1 -0
  420. data/app/views/command_tower/messaging/rendering/sms.text.erb +1 -0
  421. data/app/views/command_tower/password_reset_mailer/reset_password.html.erb +107 -0
  422. data/app/views/layouts/mailer.html.erb +13 -0
  423. data/app/views/layouts/mailer.text.erb +1 -0
  424. data/app/workflows/command_tower/workflows/admin/messaging/create_announcement_workflow.rb +62 -0
  425. data/app/workflows/command_tower/workflows/application_workflow.rb +68 -0
  426. data/app/workflows/command_tower/workflows/auth/authenticate_request_workflow.rb +40 -0
  427. data/app/workflows/command_tower/workflows/auth/authentication_response_effects.rb +21 -0
  428. data/app/workflows/command_tower/workflows/auth/authorize_request_workflow.rb +28 -0
  429. data/app/workflows/command_tower/workflows/auth/email_availability_workflow.rb +39 -0
  430. data/app/workflows/command_tower/workflows/auth/email_verification/send_workflow.rb +32 -0
  431. data/app/workflows/command_tower/workflows/auth/email_verification/verify_workflow.rb +35 -0
  432. data/app/workflows/command_tower/workflows/auth/identity_error_status.rb +36 -0
  433. data/app/workflows/command_tower/workflows/auth/identity_policy_workflow.rb +26 -0
  434. data/app/workflows/command_tower/workflows/auth/logout_workflow.rb +19 -0
  435. data/app/workflows/command_tower/workflows/auth/password_recovery_session/authenticate_workflow.rb +49 -0
  436. data/app/workflows/command_tower/workflows/auth/password_recovery_session/create_workflow.rb +39 -0
  437. data/app/workflows/command_tower/workflows/auth/password_reset/reset_workflow.rb +40 -0
  438. data/app/workflows/command_tower/workflows/auth/password_reset/send_workflow.rb +45 -0
  439. data/app/workflows/command_tower/workflows/auth/password_reset/validate_workflow.rb +40 -0
  440. data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +46 -0
  441. data/app/workflows/command_tower/workflows/auth/register_workflow.rb +83 -0
  442. data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +44 -0
  443. data/app/workflows/command_tower/workflows/auth/session_error_status.rb +30 -0
  444. data/app/workflows/command_tower/workflows/auth/signup_error_status.rb +31 -0
  445. data/app/workflows/command_tower/workflows/auth/signup_session/authenticate_workflow.rb +49 -0
  446. data/app/workflows/command_tower/workflows/auth/signup_session/create_workflow.rb +37 -0
  447. data/app/workflows/command_tower/workflows/auth/username_availability_workflow.rb +39 -0
  448. data/app/workflows/command_tower/workflows/me/change_password_workflow.rb +34 -0
  449. data/app/workflows/command_tower/workflows/me/clear_phone_workflow.rb +52 -0
  450. data/app/workflows/command_tower/workflows/me/error_mapping.rb +46 -0
  451. data/app/workflows/command_tower/workflows/me/error_status.rb +28 -0
  452. data/app/workflows/command_tower/workflows/me/phone_verification/send_workflow.rb +65 -0
  453. data/app/workflows/command_tower/workflows/me/phone_verification/verify_workflow.rb +57 -0
  454. data/app/workflows/command_tower/workflows/me/pushover/create_workflow.rb +38 -0
  455. data/app/workflows/command_tower/workflows/me/pushover/destroy_workflow.rb +34 -0
  456. data/app/workflows/command_tower/workflows/me/pushover/replace_workflow.rb +38 -0
  457. data/app/workflows/command_tower/workflows/me/pushover/show_workflow.rb +34 -0
  458. data/app/workflows/command_tower/workflows/me/pushover/verify_workflow.rb +34 -0
  459. data/app/workflows/command_tower/workflows/me/pushover/workflow_support.rb +31 -0
  460. data/app/workflows/command_tower/workflows/me/show_workflow.rb +33 -0
  461. data/app/workflows/command_tower/workflows/me/update_name_workflow.rb +48 -0
  462. data/app/workflows/command_tower/workflows/me/update_phone_workflow.rb +55 -0
  463. data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +60 -0
  464. data/app/workflows/command_tower/workflows/messaging/error_mapping.rb +26 -0
  465. data/app/workflows/command_tower/workflows/messaging/execution/deliver_workflow.rb +234 -0
  466. data/app/workflows/command_tower/workflows/messaging/handoff/process_workflow.rb +90 -0
  467. data/app/workflows/command_tower/workflows/messaging/inbox.rb +97 -0
  468. data/app/workflows/command_tower/workflows/messaging/preferences/show_workflow.rb +31 -0
  469. data/app/workflows/command_tower/workflows/messaging/preferences/update_workflow.rb +37 -0
  470. data/app/workflows/command_tower/workflows/profile/show_workflow.rb +27 -0
  471. data/app/workflows/command_tower/workflows/workflow_result.rb +34 -0
  472. data/config/routes.rb +85 -37
  473. data/db/migrate/{20241117043720_create_command_tower_users.rb → 20260805000001_create_command_tower_users.rb} +7 -9
  474. data/db/migrate/{20241204065708_create_command_tower_user_secrets.rb → 20260805000002_create_command_tower_user_secrets.rb} +3 -0
  475. data/db/migrate/20260805000003_create_command_tower_messaging_core.rb +90 -0
  476. data/db/migrate/20260805000004_create_messaging_notification_preferences.rb +17 -0
  477. data/db/migrate/20260805000005_create_messaging_endpoints_and_pushover_credentials.rb +72 -0
  478. data/docs/api_reference.md +438 -0
  479. data/docs/architecture.md +30 -0
  480. data/docs/authentication.md +37 -0
  481. data/docs/authentication_authorization_guide.md +150 -0
  482. data/docs/authorization.md +34 -0
  483. data/docs/change_password_workflow.md +67 -0
  484. data/docs/controllers.md +35 -0
  485. data/docs/cookie_authentication_guide.md +167 -0
  486. data/docs/email_verification_workflow.md +58 -0
  487. data/docs/extending.md +105 -0
  488. data/docs/host_integration_guide.md +158 -0
  489. data/docs/initializing.md +169 -0
  490. data/docs/messaging_integration_guide.md +87 -0
  491. data/docs/models.md +51 -0
  492. data/docs/pagination.md +47 -0
  493. data/docs/password_reset_workflow.md +82 -0
  494. data/docs/sensitive_routes.md +47 -0
  495. data/docs/testing.md +74 -0
  496. data/docs/upgrades/0.10.0.md +58 -0
  497. data/docs/upgrades/README.md +9 -0
  498. data/lib/command_tower/authorization/default.yml +5 -33
  499. data/lib/command_tower/configuration/application/config.rb +2 -2
  500. data/lib/command_tower/configuration/base.rb +0 -1
  501. data/lib/command_tower/configuration/config.rb +41 -5
  502. data/lib/command_tower/configuration/credentials/config.rb +33 -0
  503. data/lib/command_tower/configuration/credentials/smtp.rb +33 -0
  504. data/lib/command_tower/configuration/credentials/twilio.rb +33 -0
  505. data/lib/command_tower/configuration/email/config.rb +1 -18
  506. data/lib/command_tower/configuration/identity/config.rb +19 -0
  507. data/lib/command_tower/configuration/identity/phone_verification.rb +54 -0
  508. data/lib/command_tower/configuration/jwt/config.rb +7 -0
  509. data/lib/command_tower/configuration/jwt/cookie/config.rb +64 -0
  510. data/lib/command_tower/configuration/jwt/cookie/csrf/config.rb +72 -0
  511. data/lib/command_tower/configuration/login/strategy/plain_text/config.rb +6 -0
  512. data/lib/command_tower/configuration/login/strategy/plain_text/email_verify.rb +5 -0
  513. data/lib/command_tower/configuration/login/strategy/plain_text/password_reset.rb +49 -0
  514. data/lib/command_tower/configuration/messaging/config.rb +60 -0
  515. data/lib/command_tower/configuration/messaging/pushover.rb +42 -0
  516. data/lib/command_tower/configuration/messaging/sms.rb +32 -0
  517. data/lib/command_tower/configuration/pagination/config.rb +16 -0
  518. data/lib/command_tower/configuration/password_recovery_session/config.rb +61 -0
  519. data/lib/command_tower/configuration/password_recovery_session/rate_limits.rb +33 -0
  520. data/lib/command_tower/configuration/signup_session/config.rb +67 -0
  521. data/lib/command_tower/configuration/signup_session/email_availability.rb +18 -0
  522. data/lib/command_tower/configuration/signup_session/rate_limits.rb +48 -0
  523. data/lib/command_tower/configuration/user/config.rb +6 -5
  524. data/lib/command_tower/credential_resolution/env_backend.rb +25 -0
  525. data/lib/command_tower/credential_resolution/smtp_action_mailer_bridge.rb +24 -0
  526. data/lib/command_tower/credential_resolution/smtp_credentials.rb +26 -0
  527. data/lib/command_tower/credential_resolution/twilio_credentials.rb +26 -0
  528. data/lib/command_tower/credential_resolution.rb +78 -0
  529. data/lib/command_tower/current.rb +7 -0
  530. data/lib/command_tower/engine.rb +4 -1
  531. data/lib/command_tower/install/baseline.rb +31 -0
  532. data/lib/command_tower/install/doctor.rb +227 -0
  533. data/lib/command_tower/jwt/authorization_helper.rb +136 -0
  534. data/lib/command_tower/jwt/csrf_helper.rb +138 -0
  535. data/lib/command_tower/password_recovery/authorization_helper.rb +25 -0
  536. data/lib/command_tower/redis_connection.rb +21 -0
  537. data/lib/command_tower/schema/error/email_validation_required.rb +15 -0
  538. data/lib/command_tower/schema/shared/user.rb +32 -0
  539. data/lib/command_tower/schema.rb +2 -25
  540. data/lib/command_tower/signup/authorization_helper.rb +25 -0
  541. data/lib/command_tower/spec_helper.rb +58 -1
  542. data/lib/command_tower/testing.rb +62 -0
  543. data/lib/command_tower/version.rb +1 -1
  544. data/lib/command_tower.rb +20 -0
  545. data/lib/generators/command_tower/configure/USAGE +20 -0
  546. data/lib/generators/command_tower/configure/configure_generator.rb +39 -0
  547. data/lib/tasks/auto_annotate_models.rake +23 -55
  548. data/lib/tasks/install.rake +81 -0
  549. data/lib/tasks/users.rake +119 -0
  550. data/spec/factories/entity.rb +37 -0
  551. data/spec/factories/messaging.rb +156 -0
  552. data/spec/factories/role.rb +29 -0
  553. data/spec/factories/user.rb +53 -0
  554. data/spec/factories/user_secret.rb +12 -0
  555. metadata +579 -53
  556. data/app/controllers/command_tower/admin_controller.rb +0 -104
  557. data/app/controllers/command_tower/auth/plain_text_controller.rb +0 -132
  558. data/app/controllers/command_tower/inbox/message_blast_controller.rb +0 -89
  559. data/app/controllers/command_tower/inbox/message_controller.rb +0 -79
  560. data/app/controllers/command_tower/user_controller.rb +0 -49
  561. data/app/controllers/command_tower/username_controller.rb +0 -26
  562. data/app/helpers/command_tower/schema_helper.rb +0 -29
  563. data/app/models/message.rb +0 -30
  564. data/app/models/message_blast.rb +0 -27
  565. data/app/services/command_tower/inbox_service/blast/delete.rb +0 -23
  566. data/app/services/command_tower/inbox_service/blast/metadata.rb +0 -26
  567. data/app/services/command_tower/inbox_service/blast/new_user_blaster.rb +0 -24
  568. data/app/services/command_tower/inbox_service/blast/retrieve.rb +0 -30
  569. data/app/services/command_tower/inbox_service/blast/upsert.rb +0 -67
  570. data/app/services/command_tower/inbox_service/message/metadata.rb +0 -35
  571. data/app/services/command_tower/inbox_service/message/modify.rb +0 -44
  572. data/app/services/command_tower/inbox_service/message/retrieve.rb +0 -36
  573. data/app/services/command_tower/inbox_service/message/send.rb +0 -33
  574. data/app/services/command_tower/jwt/time_delay_token.rb +0 -17
  575. data/app/services/command_tower/login_strategy/plain_text/create.rb +0 -43
  576. data/app/services/command_tower/login_strategy/plain_text/email_verification/generate.rb +0 -29
  577. data/app/services/command_tower/login_strategy/plain_text/email_verification/send.rb +0 -23
  578. data/app/services/command_tower/login_strategy/plain_text/email_verification/verify.rb +0 -24
  579. data/app/services/command_tower/login_strategy/plain_text/login.rb +0 -50
  580. data/app/services/command_tower/user_attributes/modify.rb +0 -68
  581. data/app/services/command_tower/user_attributes/roles.rb +0 -27
  582. data/db/migrate/20250223023306_create_command_tower_messages.rb +0 -12
  583. data/db/migrate/20250223023313_create_command_tower_message_blasts.rb +0 -14
  584. data/lib/command_tower/schema/admin/users.rb +0 -15
  585. data/lib/command_tower/schema/inbox/blast_request.rb +0 -15
  586. data/lib/command_tower/schema/inbox/blast_response.rb +0 -16
  587. data/lib/command_tower/schema/inbox/message_blast_entity.rb +0 -16
  588. data/lib/command_tower/schema/inbox/message_blast_metadata.rb +0 -16
  589. data/lib/command_tower/schema/inbox/message_entity.rb +0 -14
  590. data/lib/command_tower/schema/inbox/metadata.rb +0 -18
  591. data/lib/command_tower/schema/inbox/modified.rb +0 -13
  592. data/lib/command_tower/schema/page.rb +0 -14
  593. data/lib/command_tower/schema/plain_text/create_user_request.rb +0 -18
  594. data/lib/command_tower/schema/plain_text/create_user_response.rb +0 -17
  595. data/lib/command_tower/schema/plain_text/email_verify_request.rb +0 -11
  596. data/lib/command_tower/schema/plain_text/email_verify_response.rb +0 -11
  597. data/lib/command_tower/schema/plain_text/email_verify_send_request.rb +0 -9
  598. data/lib/command_tower/schema/plain_text/email_verify_send_response.rb +0 -11
  599. data/lib/command_tower/schema/plain_text/login_request.rb +0 -15
  600. data/lib/command_tower/schema/plain_text/login_response.rb +0 -13
  601. data/lib/command_tower/schema/user.rb +0 -28
  602. data/lib/generators/api_engine_base/configure/USAGE +0 -8
  603. data/lib/generators/api_engine_base/configure/configure_generator.rb +0 -12
@@ -0,0 +1,169 @@
1
+ # Initializing CommandTower
2
+
3
+ CommandTower is a Rails engine. The install flow below gets the engine **mounted, migrated, and doctor-checked**. That alone is not a fully usable Me/Auth host — complete RBAC, feature gates, and a smoke check via the [Host integration guide](host_integration_guide.md).
4
+
5
+ Back to [README](../README.md).
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ # Gemfile: gem "command_tower" (or path/git source)
11
+ bundle install
12
+
13
+ bin/rails command_tower:install
14
+ bin/rails db:migrate
15
+ bin/rails command_tower:doctor
16
+ ```
17
+
18
+ `command_tower:install` does **not** run `db:migrate`. Installing and migrating stay separate on purpose.
19
+
20
+ ### After install
21
+
22
+ Continue with [Host integration](host_integration_guide.md):
23
+
24
+ 1. Host `rbac_groups.yml` (required — otherwise authenticated Me/Auth calls **403**)
25
+ 2. Assign host roles (for example `member`) on users
26
+ 3. Enable feature gates you need (login, reset, availability, …)
27
+ 4. Smoke-check `GET /me` with a Bearer token
28
+ 5. Wire messaging catalog/adapters when you emit or use phone/Pushover
29
+
30
+ ### What `command_tower:install` does
31
+
32
+ 1. Runs Rails-native `command_tower:install:migrations` (copies engine migrations into the host `db/migrate/` with `*.command_tower.rb` scope).
33
+ 2. Runs `rails g command_tower:configure` unless skipped (creates initializer; mounts the engine).
34
+ 3. Prints next steps.
35
+
36
+ ### Flags / environment
37
+
38
+ | Variable | Effect |
39
+ |----------|--------|
40
+ | `SKIP_CONFIGURE=1` | Migrations only (existing custom initializer / mount) |
41
+ | `SKIP_MOUNT=1` | Generate initializer but do not mount routes |
42
+ | `FORCE=1` | Overwrite an existing `config/initializers/command_tower.rb` |
43
+
44
+ Examples:
45
+
46
+ ```bash
47
+ SKIP_CONFIGURE=1 bin/rails command_tower:install
48
+ SKIP_MOUNT=1 bin/rails command_tower:install
49
+ FORCE=1 bin/rails command_tower:install
50
+ ```
51
+
52
+ ## Configure generator (optional)
53
+
54
+ Prefer `command_tower:install` for greenfield hosts. The generator remains available:
55
+
56
+ ```bash
57
+ bin/rails generate command_tower:configure
58
+ bin/rails generate command_tower:configure --skip-routes
59
+ bin/rails generate command_tower:configure --force
60
+ ```
61
+
62
+ The generator:
63
+
64
+ 1. Adds `config/initializers/command_tower.rb` when missing (or overwrites with `--force`).
65
+ 2. Mounts `CommandTower::Engine` unless `--skip-routes` or already mounted.
66
+
67
+ Dummy-host example initializer (engine development): [`rails_app/config/initializers/command_tower.rb`](../rails_app/config/initializers/command_tower.rb).
68
+
69
+ ## Database migrations
70
+
71
+ CommandTower is the **sole authoring authority** for CommandTower-owned schema (`db/migrate` inside the gem). Hosts must **not** manually write, edit, or copy-paste CommandTower schema migrations (**AD-SCH-01**).
72
+
73
+ ### Fresh host install
74
+
75
+ Handled by `command_tower:install` (step 1). Equivalent migration-only command:
76
+
77
+ ```bash
78
+ bin/rails command_tower:install:migrations
79
+ bin/rails db:migrate
80
+ ```
81
+
82
+ `command_tower:install:migrations` is the standard Rails engine task (wrapper around `railties:install:migrations`). Re-running installation is **idempotent**: already-installed CommandTower migrations are skipped.
83
+
84
+ Rails may **retimestamp** newly copied host files. That is normal. Do not hand-edit installed `*.command_tower.rb` files.
85
+
86
+ ### Upgrade workflow
87
+
88
+ 1. Bump/release the CommandTower gem dependency in the host.
89
+ 2. Run `bin/rails command_tower:install:migrations` (or `SKIP_CONFIGURE=1 bin/rails command_tower:install`).
90
+ 3. Run `bin/rails db:migrate`.
91
+ 4. Optionally run `bin/rails command_tower:doctor`.
92
+
93
+ Do not re-run configure on hosts that already customize the initializer unless you intend to regenerate it (`FORCE=1`).
94
+
95
+ ### Adding future migrations
96
+
97
+ 1. Author the migration **only** in CommandTower `db/migrate/`.
98
+ 2. Release / bump the gem in the host.
99
+ 3. Install + migrate as above.
100
+
101
+ Installed host copies are an **execution context**, not a second authoring home.
102
+
103
+ ## Configuration
104
+
105
+ Required for production-ready hosts:
106
+
107
+ - `config.jwt.hmac_secret` — typically `SECRET_KEY_BASE` / `Rails.application.secret_key_base`
108
+ - `config.signup_session.jwt_secret` — or `SIGNUP_SESSION_JWT_SECRET`
109
+ - `config.password_recovery_session.jwt_secret` — or `PASSWORD_RECOVERY_SESSION_JWT_SECRET`
110
+
111
+ Optional / feature-gated:
112
+
113
+ - Messaging SMS / Pushover adapters and credential resolution (`config.credentials.*` or ENV)
114
+ - Cookie + CSRF JWT modes
115
+ - Admin enablement, application URL, welcome content hooks
116
+
117
+ The generated initializer documents available options via class_composer. Defaults live in CommandTower; do not duplicate them unnecessarily in the host.
118
+
119
+ ## Doctor
120
+
121
+ ```bash
122
+ bin/rails command_tower:doctor
123
+ ```
124
+
125
+ Checks Rails version compatibility, engine baseline migrations, host-installed migration copies, JWT / session secrets, and messaging adapter names. Failures abort with remediation text; warnings print but allow success.
126
+
127
+ Doctor does **not** probe Redis/SMTP connectivity.
128
+
129
+ ## Host vs CommandTower responsibilities
130
+
131
+ | Concern | Owner |
132
+ |---------|--------|
133
+ | Author CT schema migrations | CommandTower |
134
+ | Install / run migrations | Host (via engine tasks + `db:migrate`) |
135
+ | Product configuration / secrets | Host initializer + ENV |
136
+ | Dual-author CT schema in the host | **Forbidden** |
137
+
138
+ ## Troubleshooting
139
+
140
+ | Symptom | Fix |
141
+ |---------|-----|
142
+ | `users already exists` on migrate | Database not empty / stale `schema.rb` load — migrate an empty DB, or dump schema after a clean migrate |
143
+ | Install copies nothing new | Already installed — expected idempotency |
144
+ | Missing tables after install | You installed but did not `db:migrate` |
145
+ | Doctor fails on JWT default | Set `config.jwt.hmac_secret` from a real secret |
146
+ | Accidental public engine mount | Use `SKIP_MOUNT=1` / `--skip-routes`, or mount at the path your product needs |
147
+
148
+ ## Existing customized hosts
149
+
150
+ If the host already has a bespoke initializer and mount:
151
+
152
+ ```bash
153
+ SKIP_CONFIGURE=1 bin/rails command_tower:install
154
+ # same as: bin/rails command_tower:install:migrations
155
+ bin/rails db:migrate
156
+ ```
157
+
158
+ Do not run `rails g command_tower:configure` unless you intentionally want stock scaffolding.
159
+
160
+ ## Test suite factories
161
+
162
+ Shared FactoryBot definitions ship with the gem. In the host test boot path:
163
+
164
+ ```ruby
165
+ require "command_tower/testing"
166
+ CommandTower::Testing.install!
167
+ ```
168
+
169
+ Then extend with `FactoryBot.modify` as needed. See [Testing with CommandTower](testing.md).
@@ -0,0 +1,87 @@
1
+ # Messaging with CommandTower
2
+
3
+ CommandTower owns modern messaging **platform** surfaces. Hosts supply product content, channel policy, and adapter credentials.
4
+
5
+ Complete install and host RBAC first: [Host integration](host_integration_guide.md) (Steps 1–8 before emitting or wiring phone/Pushover).
6
+
7
+ ## Current surfaces
8
+
9
+ | Concern | Path / entry |
10
+ |---------|----------------|
11
+ | User inbox (consume) | Engine `/me/inbox*` — see [API reference](api_reference.md#me-inbox) |
12
+ | Preferences | Engine `/me/preferences*` — see [API reference](api_reference.md#preferences) |
13
+ | Phone / Pushover endpoints | Engine `/me/phone*`, `/me/pushover*` (503 when capability unavailable) |
14
+ | Single-recipient emit | `CommandTower::Services::Messaging::Communications::Produce` |
15
+ | Multi-recipient emit | `CommandTower::Services::Messaging::Communications::ProduceMany` |
16
+ | Admin cohort announce | Engine `POST /admin/messaging/announcements` |
17
+ | Host ops announce | Host-owned rake/job (product-specific; not part of the gem) |
18
+
19
+ Authentication uses JWT bearer or cookie session as documented in [Authentication](authentication.md). Me Inbox authorization requires host RBAC entities (dummy host uses a `member` role mapping). Admin announcements use entity `admin_messaging_announcements` (engine `default.yml` admin group).
20
+
21
+ ## Produce (single recipient)
22
+
23
+ Call from a **product workflow**, not from a controller service bypass:
24
+
25
+ ```ruby
26
+ CommandTower::Services::Messaging::Communications::Produce.call(
27
+ user: user,
28
+ notification_type_key: "welcome",
29
+ host_event_identity: "welcome/#{user.id}/#{SecureRandom.uuid}",
30
+ title: "Welcome",
31
+ body: "Thanks for joining.",
32
+ platform_enabled_channels: [:inbox, :email],
33
+ metadata: { source: "onboarding" } # optional
34
+ )
35
+ ```
36
+
37
+ | Kwarg | Required |
38
+ |-------|----------|
39
+ | `user` | yes (`User`) |
40
+ | `notification_type_key` | yes (`String`) |
41
+ | `host_event_identity` | yes (`String`) |
42
+ | `title` | yes |
43
+ | `body` | yes |
44
+ | `platform_enabled_channels` | yes (`Array`) |
45
+ | `metadata` | no (`Hash`) |
46
+
47
+ ## ProduceMany (multi recipient)
48
+
49
+ | Kwarg | Required / notes |
50
+ |-------|------------------|
51
+ | `user_ids` | yes (`Array` of integer ids) |
52
+ | `notification_type_key` | yes |
53
+ | `campaign_identity` | yes — fan-out builds `host_event_identity` as `"#{campaign_identity}/#{user.id}"` |
54
+ | `title`, `body` | yes |
55
+ | `platform_enabled_channels` | yes |
56
+ | `metadata` | optional |
57
+ | `execution_mode` | optional, default `:async`; `:sync` capped at **25** recipients |
58
+
59
+ Admin announcements HTTP is a product path over ProduceMany (async/sync, audience selection). Contract: [API reference — Admin messaging](api_reference.md#admin-messaging).
60
+
61
+ ## Me Inbox HTTP (summary)
62
+
63
+ | Concern | Contract |
64
+ |---------|----------|
65
+ | List | `GET /me/inbox?limit=&offset=&scope=` — meta `{ limit, offset, totalCount }` |
66
+ | Detail / open / archive / delete | `/me/inbox/:id` (+ `open`, `archive`) |
67
+ | Bulk | `POST /me/inbox/bulk/{read,unread,archive,restore,delete}` with body `ids` |
68
+ | Unread | `GET /me/inbox/unread-count` |
69
+
70
+ Pagination detail: [pagination.md](pagination.md). Full catalog: [api_reference.md](api_reference.md#me-inbox).
71
+
72
+ ## Host responsibilities
73
+
74
+ - Notification catalog content (registered into CommandTower notification types)
75
+ - `platform_enabled_channels` / channel policy injection
76
+ - Messaging adapter credentials (email / SMS / Pushover) via initializer or ENV
77
+ - Host `rbac_groups.yml` entities for Me inbox/preferences/phone/pushover (and admin if used)
78
+ - Product-specific operational tooling (announce rake tasks, welcome copy)
79
+
80
+ ## Related
81
+
82
+ - [Controllers / routes](controllers.md)
83
+ - [API reference](api_reference.md)
84
+ - [Extending](extending.md) — Produce from host workflows; Do-Not-Extend
85
+ - [Initializing](initializing.md) — configuration and doctor
86
+ - [Pagination](pagination.md)
87
+ - [README](../README.md)
data/docs/models.md ADDED
@@ -0,0 +1,51 @@
1
+ # CommandTower Models
2
+
3
+ Core models live in the root / CommandTower namespaces and support authentication, authorization, and messaging ledger associations.
4
+
5
+ Back to [README](../README.md).
6
+
7
+ ## User
8
+
9
+ `User` is the primary identity model for host applications.
10
+
11
+ It underpins [Authentication](authentication.md) and [Authorization](authorization.md), and participates in messaging via associations such as `messaging_communications`.
12
+
13
+ Sensitive session invalidation uses the user’s verifier token — see [Sensitive changes](sensitive_routes.md).
14
+
15
+ ### Reopening `User` in the host
16
+
17
+ Prefer services/workflows for new behavior. When you must reopen the class:
18
+
19
+ ```ruby
20
+ require CommandTower::Engine.root.join("app", "models", "user.rb")
21
+
22
+ class User
23
+ def self.my_class_method; end
24
+ def my_instance_method; end
25
+ end
26
+ ```
27
+
28
+ ## UserSecret
29
+
30
+ `UserSecret` backs validation and recovery-related secrets (for example plain-text email validation flows). It complements [Sensitive changes](sensitive_routes.md).
31
+
32
+ ### Reopening `UserSecret` in the host
33
+
34
+ ```ruby
35
+ require CommandTower::Engine.root.join("app", "models", "user_secret.rb")
36
+
37
+ class UserSecret
38
+ def self.my_class_method; end
39
+ def my_instance_method; end
40
+ end
41
+ ```
42
+
43
+ ## Messaging models
44
+
45
+ Engine-owned messaging persistence (communications, endpoints, preferences, and related records) ships with CommandTower. Prefer platform services/workflows for mutations; do not dual-author schema in the host ([Initializing](initializing.md)).
46
+
47
+ ## Related
48
+
49
+ - [Architecture](architecture.md)
50
+ - [Sensitive changes](sensitive_routes.md)
51
+ - [Testing](testing.md) — shared factories for CT-owned models
@@ -0,0 +1,47 @@
1
+ # Pagination
2
+
3
+ CommandTower exposes one live HTTP list contract that returns pagination metadata: **Me Inbox**.
4
+
5
+ Back to [README](../README.md).
6
+
7
+ ## Me Inbox list contract
8
+
9
+ `GET /me/inbox` accepts query parameters:
10
+
11
+ | Param | Default | Notes |
12
+ |-------|---------|--------|
13
+ | `limit` | `50` | Max `100` |
14
+ | `offset` | `0` | |
15
+ | `scope` | `"inbox"` | `"inbox"` or `"archived"` |
16
+
17
+ Success envelope `meta`:
18
+
19
+ ```json
20
+ {
21
+ "limit": 50,
22
+ "offset": 0,
23
+ "totalCount": 123
24
+ }
25
+ ```
26
+
27
+ `data` is an array of inbox items. Full route table: [API reference — Me Inbox](api_reference.md#me-inbox).
28
+
29
+ Deserializer: `CommandTower::Deserializers::Messaging::Inbox::ListDeserializer`.
30
+
31
+ ## Global pagination config
32
+
33
+ `CommandTower.config.pagination.limit` defaults to **10**. That value is a generic configuration default for pagination helpers — it is **not** the Me Inbox HTTP default (Inbox uses 50).
34
+
35
+ ```ruby
36
+ CommandTower.configure do |c|
37
+ c.pagination.limit = 50
38
+ end
39
+ ```
40
+
41
+ There is no engine admin list endpoint and no `pagination=true` / `page` / `cursor` query API on current engine HTTP surfaces.
42
+
43
+ ## Related
44
+
45
+ - [API reference](api_reference.md)
46
+ - [Messaging](messaging_integration_guide.md)
47
+ - [Controllers](controllers.md)
@@ -0,0 +1,82 @@
1
+ # Password Reset (Forgot Password) Workflow
2
+
3
+ Public password recovery for users who cannot authenticate. Flow uses a **password-recovery session** plus gated **password-reset** endpoints. Detailed schemas live here and are summarized in the [API reference](api_reference.md).
4
+
5
+ ## Purpose and orchestration
6
+
7
+ | Step | Route | Workflow |
8
+ |------|-------|----------|
9
+ | Create recovery session | `POST /auth/password-recovery-session` | `Workflows::Auth::PasswordRecoverySession::CreateWorkflow` |
10
+ | Send reset email | `POST /auth/password-reset/send` | `Workflows::Auth::PasswordReset::SendWorkflow` |
11
+ | Validate token | `POST /auth/password-reset/validate` | `Workflows::Auth::PasswordReset::ValidateWorkflow` |
12
+ | Reset password | `POST /auth/password-reset/reset` | `Workflows::Auth::PasswordReset::ResetWorkflow` |
13
+
14
+ All listed workflows inherit `ApplicationWorkflow` with `retry_strategy :none`. Controllers under `CommandTower::Auth::PasswordReset::*` and password-recovery-session authenticate the recovery session where required, deserialize, run **one** workflow, and render `WorkflowResult`.
15
+
16
+ **Feature gate:** `CommandTower.config.login.plain_text.password_reset?` (reset endpoints). Recovery session creation is part of the public recovery path.
17
+
18
+ **Boundary:** Controllers own transport + recovery-session auth. Workflows orchestrate rate limits and password-reset services. Services own email/token capability details. Do not call messaging execution internals from hosts — see [extending.md](extending.md).
19
+
20
+ ## High-level characteristics
21
+
22
+ - **No user JWT**; `password-reset/send` requires a valid **password-recovery session**; `validate` / `reset` are public and use the emailed reset `token` in the body
23
+ - Email-based identification; responses avoid user enumeration where designed to
24
+ - Secure, time-limited reset tokens
25
+ - Rate limiting via password-recovery rate-limit services
26
+
27
+ ## Endpoints (engine-relative)
28
+
29
+ ### 1. Create password-recovery session
30
+
31
+ `POST /auth/password-recovery-session`
32
+
33
+ Establishes the recovery session used by subsequent reset calls. See request specs under `spec/requests/` for exact payload/envelope.
34
+
35
+ ### 2. Send reset email
36
+
37
+ `POST /auth/password-reset/send`
38
+
39
+ **Auth:** Password-recovery session
40
+
41
+ **Body (typical):** `{ "email": "string" }`
42
+
43
+ **Workflow:** `SendWorkflow` → rate-limit check → `Services::Auth::PasswordReset::Send`
44
+
45
+ ### 3. Validate token
46
+
47
+ `POST /auth/password-reset/validate`
48
+
49
+ **Auth:** Public (reset token in body)
50
+
51
+ **Body (typical):** `{ "token": "string" }` (optional `email` when config requires it)
52
+
53
+ **Workflow:** `ValidateWorkflow`
54
+
55
+ ### 4. Reset password
56
+
57
+ `POST /auth/password-reset/reset`
58
+
59
+ **Auth:** Public (reset token in body)
60
+
61
+ **Body (typical):** token + new password fields (`passwordConfirmation` / `password_confirmation`)
62
+
63
+ **Workflow:** `ResetWorkflow`
64
+
65
+ Success behavior and verifier rotation differ from authenticated [change password](change_password_workflow.md) — treat recovery success per product policy (often force Sign In).
66
+
67
+ ## Errors
68
+
69
+ Failures return the standard application envelope via `render_application_result`. Status mapping uses Auth identity/password-reset error helpers. Validation failures from deserializers typically surface as `422 Unprocessable Entity`.
70
+
71
+ ## Security notes
72
+
73
+ - Never log passwords, raw tokens, or recovery-session secrets
74
+ - Prefer HTTPS in production
75
+ - Respect rate limits; do not probe for account existence in client UX
76
+
77
+ ## Related
78
+
79
+ - [Change password workflow](change_password_workflow.md)
80
+ - [API reference](api_reference.md)
81
+ - [Initializing](initializing.md) — secrets / feature config
82
+ - [Extending](extending.md)
@@ -0,0 +1,47 @@
1
+ # Sensitive changes and session invalidation
2
+
3
+ CommandTower JWTs are bound to the authenticated user’s **verifier token**. When the verifier rotates, previously issued access tokens that carried the old verifier no longer authenticate (`401`).
4
+
5
+ This is the platform mechanism behind “logout everywhere” and post-password-change session invalidation.
6
+
7
+ ## Verifier token on `User`
8
+
9
+ Each `User` may store:
10
+
11
+ - `verifier_token` — opaque string embedded in JWTs at issue time
12
+ - `verifier_token_last_reset` — timestamp of the last rotation
13
+
14
+ API:
15
+
16
+ ```ruby
17
+ user.reset_verifier_token! # generates a new token, updates timestamp, returns the value
18
+ user.retreive_verifier_token! # returns existing token or creates one (historical spelling)
19
+ ```
20
+
21
+ Authentication compares the verifier claim in the JWT to the user’s current `verifier_token`. A mismatch fails authentication.
22
+
23
+ ## When the platform rotates the verifier
24
+
25
+ | Event | Behavior |
26
+ |-------|----------|
27
+ | Authenticated password change (`PATCH /me/password`) | Rotates verifier so prior tokens fail |
28
+ | Host / ops call `user.reset_verifier_token!` | Explicit revoke-all-sessions |
29
+
30
+ There is no engine HTTP admin “mutate user with `verifier_token: true`” route. `UserAttributes::Mutate` may still exist for internal/host use; do not assume a public SchemaHelper admin path.
31
+
32
+ Browser `POST /auth/logout` clears cookie/session transport for that client; it does **not** by itself rotate `verifier_token` for all devices. Use verifier rotation when all outstanding JWTs must die.
33
+
34
+ ## Host guidance
35
+
36
+ - Treat password changes and explicit “revoke all sessions” as **sensitive** operations that should rotate the verifier.
37
+ - Do not expose `verifier_token` in API payloads (platform serializers omit it).
38
+ - Prefer platform workflows/services for password change rather than hand-editing password digests without rotation.
39
+ - `UserSecret` backs related validation/recovery secrets; see [Models](models.md).
40
+
41
+ ## Related
42
+
43
+ - [Change password workflow](change_password_workflow.md)
44
+ - [Authentication](authentication.md) / [Authentication & authorization guide](authentication_authorization_guide.md)
45
+ - [Cookie authentication](cookie_authentication_guide.md)
46
+ - [Models](models.md)
47
+ - [README](../README.md)
data/docs/testing.md ADDED
@@ -0,0 +1,74 @@
1
+ # Testing with CommandTower
2
+
3
+ CommandTower ships FactoryBot definitions for engine-owned models. Hosts load
4
+ them explicitly in the test suite — there is no production Railtie and
5
+ FactoryBot is not a runtime gem dependency.
6
+
7
+ Back to [README](../README.md). See also [Initializing](initializing.md).
8
+
9
+ ## Setup
10
+
11
+ ```ruby
12
+ # spec/rails_helper.rb (after Rails is loaded)
13
+ require "command_tower/testing"
14
+
15
+ CommandTower::Testing.install! # preferred
16
+ # CommandTower::Testing.load_factories! # factories-only / advanced
17
+ ```
18
+
19
+ Call `install!` **before** any host `FactoryBot.modify` blocks so shared
20
+ factories exist when you extend them.
21
+
22
+ ## Extending factories
23
+
24
+ Hosts must not redefine CommandTower base factories. Use `FactoryBot.modify`:
25
+
26
+ ```ruby
27
+ FactoryBot.modify do
28
+ factory :user do
29
+ roles { ["member"] }
30
+
31
+ trait :operator do
32
+ roles { %w[member operator] }
33
+ end
34
+ end
35
+ end
36
+ ```
37
+
38
+ ## Packaging and evolution
39
+
40
+ Factory files live under `spec/factories/**` inside the gem and are included in
41
+ the gemspec. Adding or changing a shared factory is a normal CommandTower gem
42
+ bump — hosts pick it up on upgrade.
43
+
44
+ `CommandTower::Testing.load_factories!` is idempotent (safe to call more than
45
+ once). It loads each factory file under `CommandTower::Engine.root/spec/factories`
46
+ explicitly; it does not append to global `FactoryBot.definition_file_paths`.
47
+
48
+ ## Responsibilities
49
+
50
+ | Owner | Responsibility |
51
+ |-------|----------------|
52
+ | CommandTower | Base factories for CT-owned models; Testing API; packaging |
53
+ | Host | `install!` in the test boot path; product traits via `FactoryBot.modify` |
54
+
55
+ ## Factory invariant
56
+
57
+ Base factories construct **persistent model state only**. They must not invoke
58
+ Services, Workflows, ApplicationWorkflows, or external adapters. Endpoint
59
+ ciphertext uses `SecretBox` / `Fingerprinter` (crypto helpers).
60
+
61
+ For endpoint factories in environments where
62
+ `COMMAND_TOWER_MESSAGING_ENDPOINT_SECRET` is unset, you may call:
63
+
64
+ ```ruby
65
+ CommandTower::Testing.ensure_endpoint_secret!
66
+ ```
67
+
68
+ (SecretBox already falls back to a non-production test secret outside production.)
69
+
70
+ ## Contributors vs hosts
71
+
72
+ This guide is the **host Testing API**. Contributors working inside the CommandTower
73
+ repository also follow workspace test standards (`.cursor/rules/testing.mdc`) and
74
+ architecture guards under `spec/architecture/`.
@@ -0,0 +1,58 @@
1
+ # Upgrade: CommandTower 0.10.0
2
+
3
+ **From:** `0.5.0` (and earlier SchemaHelper-era hosts on `main`)
4
+ **To:** `0.10.0`
5
+
6
+ This document summarizes **all changes from `main` through the `0.10.0` release** (committed work on `schematizing` plus the modern platform working tree). It is the durable upgrade / change SST for hosts. For a sequenced blank-host path, use [Host integration](../host_integration_guide.md).
7
+
8
+ ## Breaking / host-impact
9
+
10
+ | Change | Host impact |
11
+ |--------|-------------|
12
+ | SchemaHelper Admin / User / legacy Inbox HTTP removed | No `GET /user`, `/user/modify`, `/admin/modify*`, legacy blast/message SchemaHelper routes |
13
+ | Modern response envelope | Engine HTTP returns `{ data, meta, errors }` via `render_application_result` |
14
+ | Current-user path | Use `GET /me` (and `GET /profile`); not `GET /user` |
15
+ | Login path | `POST /auth/plain-text/login` when `login.plain_text.enable?` (route absent → **404** if gate off) |
16
+ | Password change | `PATCH /me/password` (rotates verifier; does not re-issue JWT) |
17
+ | Host RBAC required | Without host `rbac_groups.yml` entity mappings, Me/Auth calls **403** (fail-closed) |
18
+ | Feature gates | Login, email verify, password reset, availability routes are config-gated |
19
+ | Schema ownership (**AD-SCH-01**) | CT authors migrations; hosts install via `command_tower:install` / `install:migrations` then `db:migrate` |
20
+ | Admin HTTP | Only `POST /admin/messaging/announcements` (no SchemaHelper user admin) |
21
+
22
+ ## Platform capabilities in 0.10.0
23
+
24
+ - **Auth:** register, plain-text login, logout, session, identity policy, signup session, email/username availability, email verification, password-recovery session + password reset
25
+ - **Me:** account show, profile, name, password, inbox (list/detail/bulk), preferences, phone, Pushover
26
+ - **Messaging emit:** `Communications::Produce` / `ProduceMany` from host workflows
27
+ - **Install:** `command_tower:install`, configure generator, `command_tower:doctor`
28
+ - **Testing:** `CommandTower::Testing.install!` shared factories
29
+ - **Architecture:** Controller → Workflow → Service; Engine uses `authenticate_request!` / `authorize_request!`
30
+
31
+ ## Documentation
32
+
33
+ - Canonical HTTP catalog: [API reference](../api_reference.md)
34
+ - Host start-here: [Host integration](../host_integration_guide.md)
35
+ - Install SST: [Initializing](../initializing.md)
36
+ - Auth/cookie/RBAC guides refreshed to modern routes
37
+ - Messaging and pagination aligned to Inbox + Produce contracts
38
+
39
+ ## Upgrade checklist (existing hosts)
40
+
41
+ 1. Bump gem to `0.10.0` and run `bin/rails command_tower:install:migrations` (or `SKIP_CONFIGURE=1 bin/rails command_tower:install`) then `bin/rails db:migrate`.
42
+ 2. Set JWT + signup/recovery session secrets; run `bin/rails command_tower:doctor`.
43
+ 3. Copy/adapt [`rails_app/config/rbac_groups.yml`](../../rails_app/config/rbac_groups.yml) into the host; do not redefine engine `owner` / `admin` groups.
44
+ 4. Ensure users who need Me surfaces have the host `member` role (or equivalent).
45
+ 5. Enable feature gates you rely on (`login.plain_text.enable`, password reset, email verify, availability).
46
+ 6. Point clients at modern paths (`/me`, `/auth/plain-text/login`, envelope parsing).
47
+ 7. Smoke: Bearer login → `GET /me` returns **200** (not **403**).
48
+ 8. If emitting messages: catalog, channels, adapters; call Produce from host workflows.
49
+
50
+ New hosts: follow [Host integration](../host_integration_guide.md) end-to-end.
51
+
52
+ ## Related
53
+
54
+ - [Initializing](../initializing.md)
55
+ - [Extending](../extending.md)
56
+ - [Messaging](../messaging_integration_guide.md)
57
+ - [Authentication & authorization guide](../authentication_authorization_guide.md)
58
+ - [README](../../README.md)
@@ -0,0 +1,9 @@
1
+ # Upgrades
2
+
3
+ Host-facing upgrade / change summaries for CommandTower releases.
4
+
5
+ | Version | Summary |
6
+ |---------|---------|
7
+ | [0.10.0](0.10.0.md) | Modern Auth/Me/Messaging platform; SchemaHelper removal; host RBAC required |
8
+
9
+ New hosts should start with [Host integration](../host_integration_guide.md).
@@ -5,38 +5,10 @@ groups:
5
5
  entities: true
6
6
  admin:
7
7
  description: |
8
- This group defines permissions for Admin Read and Write operations. Users with this role will have
9
- the ability to view and update other users states.
8
+ Platform admin role for modern Engine admin surfaces (e.g. messaging announcements).
10
9
  entities:
11
- - admin
12
- - message-blast
13
- admin-without-impersonation:
14
- description: |
15
- This group defines permissions for Admin Read and Write operations. Users with this role will have
16
- the ability to view and update other users states. However, impersonation is not permitted with this role
17
- entities:
18
- - admin-without-impersonate
19
- - message-blast
20
- admin-read-only:
21
- description: |
22
- This group defines permissions for Admin Read interface only.
23
- entities:
24
- - read-admin
25
- - message-blast-read-only
10
+ - admin_messaging_announcements
26
11
  entities:
27
- - name: message-blast
28
- controller: CommandTower::Inbox::MessageBlastController
29
- - name: message-blast-read-only
30
- controller: CommandTower::Inbox::MessageBlastController
31
- only: metadata
32
- - name: read-admin
33
- controller: CommandTower::AdminController
34
- only: show
35
- - name: admin
36
- controller: CommandTower::AdminController
37
- - name: admin-without-impersonate
38
- controller: CommandTower::AdminController
39
- except: impersonate
40
-
41
-
42
-
12
+ - name: admin_messaging_announcements
13
+ controller: CommandTower::Admin::Messaging::AnnouncementsController
14
+ only: create