command_tower 0.4.0 → 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 (604) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +102 -29
  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 +31 -1
  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/password_recovery_session/config.rb +61 -0
  518. data/lib/command_tower/configuration/password_recovery_session/rate_limits.rb +33 -0
  519. data/lib/command_tower/configuration/signup_session/config.rb +67 -0
  520. data/lib/command_tower/configuration/signup_session/email_availability.rb +18 -0
  521. data/lib/command_tower/configuration/signup_session/rate_limits.rb +48 -0
  522. data/lib/command_tower/configuration/user/config.rb +6 -5
  523. data/lib/command_tower/credential_resolution/env_backend.rb +25 -0
  524. data/lib/command_tower/credential_resolution/smtp_action_mailer_bridge.rb +24 -0
  525. data/lib/command_tower/credential_resolution/smtp_credentials.rb +26 -0
  526. data/lib/command_tower/credential_resolution/twilio_credentials.rb +26 -0
  527. data/lib/command_tower/credential_resolution.rb +78 -0
  528. data/lib/command_tower/current.rb +7 -0
  529. data/lib/command_tower/engine.rb +4 -1
  530. data/lib/command_tower/install/baseline.rb +31 -0
  531. data/lib/command_tower/install/doctor.rb +227 -0
  532. data/lib/command_tower/jwt/authorization_helper.rb +136 -0
  533. data/lib/command_tower/jwt/csrf_helper.rb +138 -0
  534. data/lib/command_tower/password_recovery/authorization_helper.rb +25 -0
  535. data/lib/command_tower/redis_connection.rb +21 -0
  536. data/lib/command_tower/schema/error/email_validation_required.rb +15 -0
  537. data/lib/command_tower/schema/shared/user.rb +32 -0
  538. data/lib/command_tower/schema.rb +2 -25
  539. data/lib/command_tower/signup/authorization_helper.rb +25 -0
  540. data/lib/command_tower/spec_helper.rb +58 -1
  541. data/lib/command_tower/testing.rb +62 -0
  542. data/lib/command_tower/version.rb +1 -1
  543. data/lib/command_tower.rb +20 -0
  544. data/lib/generators/command_tower/configure/USAGE +14 -2
  545. data/lib/generators/command_tower/configure/configure_generator.rb +29 -2
  546. data/lib/tasks/auto_annotate_models.rake +23 -55
  547. data/lib/tasks/install.rake +81 -0
  548. data/lib/tasks/users.rake +119 -0
  549. data/spec/factories/entity.rb +37 -0
  550. data/spec/factories/messaging.rb +156 -0
  551. data/spec/factories/role.rb +29 -0
  552. data/spec/factories/user.rb +53 -0
  553. data/spec/factories/user_secret.rb +12 -0
  554. metadata +576 -55
  555. data/app/controllers/command_tower/admin_controller.rb +0 -110
  556. data/app/controllers/command_tower/auth/plain_text_controller.rb +0 -133
  557. data/app/controllers/command_tower/inbox/message_blast_controller.rb +0 -89
  558. data/app/controllers/command_tower/inbox/message_controller.rb +0 -81
  559. data/app/controllers/command_tower/user_controller.rb +0 -49
  560. data/app/controllers/command_tower/username_controller.rb +0 -26
  561. data/app/helpers/command_tower/pagination_helper.rb +0 -40
  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/admin_service/users.rb +0 -27
  566. data/app/services/command_tower/inbox_service/blast/delete.rb +0 -23
  567. data/app/services/command_tower/inbox_service/blast/metadata.rb +0 -26
  568. data/app/services/command_tower/inbox_service/blast/new_user_blaster.rb +0 -24
  569. data/app/services/command_tower/inbox_service/blast/retrieve.rb +0 -30
  570. data/app/services/command_tower/inbox_service/blast/upsert.rb +0 -67
  571. data/app/services/command_tower/inbox_service/message/metadata.rb +0 -41
  572. data/app/services/command_tower/inbox_service/message/modify.rb +0 -44
  573. data/app/services/command_tower/inbox_service/message/retrieve.rb +0 -36
  574. data/app/services/command_tower/inbox_service/message/send.rb +0 -33
  575. data/app/services/command_tower/jwt/time_delay_token.rb +0 -17
  576. data/app/services/command_tower/login_strategy/plain_text/create.rb +0 -43
  577. data/app/services/command_tower/login_strategy/plain_text/email_verification/generate.rb +0 -29
  578. data/app/services/command_tower/login_strategy/plain_text/email_verification/send.rb +0 -23
  579. data/app/services/command_tower/login_strategy/plain_text/email_verification/verify.rb +0 -24
  580. data/app/services/command_tower/login_strategy/plain_text/login.rb +0 -50
  581. data/app/services/command_tower/pagination_service_helper.rb +0 -88
  582. data/app/services/command_tower/user_attributes/modify.rb +0 -68
  583. data/app/services/command_tower/user_attributes/roles.rb +0 -27
  584. data/db/migrate/20250223023306_create_command_tower_messages.rb +0 -12
  585. data/db/migrate/20250223023313_create_command_tower_message_blasts.rb +0 -14
  586. data/lib/command_tower/schema/admin/users.rb +0 -16
  587. data/lib/command_tower/schema/inbox/blast_request.rb +0 -15
  588. data/lib/command_tower/schema/inbox/blast_response.rb +0 -16
  589. data/lib/command_tower/schema/inbox/message_blast_entity.rb +0 -16
  590. data/lib/command_tower/schema/inbox/message_blast_metadata.rb +0 -16
  591. data/lib/command_tower/schema/inbox/message_entity.rb +0 -14
  592. data/lib/command_tower/schema/inbox/metadata.rb +0 -18
  593. data/lib/command_tower/schema/inbox/modified.rb +0 -13
  594. data/lib/command_tower/schema/page.rb +0 -11
  595. data/lib/command_tower/schema/pagination.rb +0 -16
  596. data/lib/command_tower/schema/plain_text/create_user_request.rb +0 -18
  597. data/lib/command_tower/schema/plain_text/create_user_response.rb +0 -17
  598. data/lib/command_tower/schema/plain_text/email_verify_request.rb +0 -11
  599. data/lib/command_tower/schema/plain_text/email_verify_response.rb +0 -11
  600. data/lib/command_tower/schema/plain_text/email_verify_send_request.rb +0 -9
  601. data/lib/command_tower/schema/plain_text/email_verify_send_response.rb +0 -11
  602. data/lib/command_tower/schema/plain_text/login_request.rb +0 -15
  603. data/lib/command_tower/schema/plain_text/login_response.rb +0 -16
  604. data/lib/command_tower/schema/user.rb +0 -28
@@ -0,0 +1,438 @@
1
+ # CommandTower API Reference
2
+
3
+ Canonical **HTTP endpoint catalog** for the CommandTower engine. Paths are engine-relative — prefix with your host mount (for example `/api`).
4
+
5
+ Health checks are **host-owned**. The engine does not expose a health route.
6
+
7
+ Install / configure / migrate / doctor: [initializing.md](initializing.md). Extending: [extending.md](extending.md). Route area index: [controllers.md](controllers.md). Messaging emit (non-HTTP): [messaging_integration_guide.md](messaging_integration_guide.md).
8
+
9
+ Proof for contracts lives primarily under `spec/requests/command_tower/`.
10
+
11
+ ## Table of Contents
12
+
13
+ 1. [Shared conventions](#shared-conventions)
14
+ 2. [Authentication mechanisms](#authentication-mechanisms)
15
+ 3. [Auth endpoints](#auth-endpoints)
16
+ 4. [Me and profile](#me-and-profile)
17
+ 5. [Me Inbox](#me-inbox)
18
+ 6. [Preferences](#preferences)
19
+ 7. [Phone](#phone)
20
+ 8. [Pushover](#pushover)
21
+ 9. [Admin messaging](#admin-messaging)
22
+ 10. [Non-HTTP emit APIs](#non-http-emit-apis)
23
+ 11. [RBAC overview](#rbac-overview)
24
+ 12. [Feature gates](#feature-gates)
25
+
26
+ ---
27
+
28
+ ## Shared conventions
29
+
30
+ ### Response envelope
31
+
32
+ Engine controllers render through `render_application_result` → `EnvelopeSerializer`.
33
+
34
+ **Success**
35
+
36
+ ```json
37
+ {
38
+ "data": {},
39
+ "meta": {},
40
+ "errors": []
41
+ }
42
+ ```
43
+
44
+ **Failure**
45
+
46
+ ```json
47
+ {
48
+ "data": null,
49
+ "meta": {},
50
+ "errors": [
51
+ { "code": "string", "message": "string", "details": {} }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ - `meta` defaults to `{}` when omitted.
57
+ - Each error always has `code` and `message`. `details` appears only when present.
58
+ - Deserializer failures typically return **422** with `validation_failed` (and `details.failures` when applicable).
59
+
60
+ This envelope is the contract for **CommandTower engine HTTP** endpoints. Host product controllers may use other shapes. Provisional host helpers (`authenticate_user!` / `authorize_user!`) can still fail via older `Schema::Error` renders — see [authentication_authorization_guide.md](authentication_authorization_guide.md).
61
+
62
+ ### Field casing
63
+
64
+ Response bodies use **camelCase** keys from serializers (`firstName`, `tokenExpiresAt`, `totalCount`, …).
65
+
66
+ Many deserializers accept both camelCase and snake_case aliases for inputs. Where only one form is accepted, it is noted per endpoint.
67
+
68
+ ### Content type
69
+
70
+ Use `Content-Type: application/json` for JSON bodies. Responses are `application/json`.
71
+
72
+ ---
73
+
74
+ ## Authentication mechanisms
75
+
76
+ | Mechanism | How |
77
+ |-----------|-----|
78
+ | Bearer JWT (default) | `Authorization: Bearer <token>` |
79
+ | Cookie JWT (optional) | HttpOnly cookie when `config.jwt.cookie.enabled` — see [cookie_authentication_guide.md](cookie_authentication_guide.md) |
80
+ | Signup session | `Authorization: Signup <token>` |
81
+ | Password recovery session | `Authorization: Recovery <token>` |
82
+
83
+ **Engine controllers** use `AuthenticationBoundary` / `AuthorizationBoundary` (`authenticate_request!` / `authorize_request!`). Failures return the envelope (`401` / `403` / `412` as applicable).
84
+
85
+ **Token / cookie side effects** (login, logout, CSRF) are applied via `response_effects` on the workflow result (for example `X-Authorization-Expire`, Set-Cookie). See cookie guide for CORS and CSRF.
86
+
87
+ Default JWT TTL is **7 days** (`config.jwt.ttl`).
88
+
89
+ ---
90
+
91
+ ## Auth endpoints
92
+
93
+ ### `POST /auth/register`
94
+
95
+ | | |
96
+ |--|--|
97
+ | **Auth** | Public |
98
+ | **Gate** | Always drawn |
99
+ | **Body** | `first_name`, `last_name`, `username`, `email`, `password`, `password_confirmation` (snake_case) |
100
+ | **Success** | **201** — `data`: `{ user, message }` where `message` is `"Account created successfully"`. No token. |
101
+ | **Errors** | `422` `validation_failed` / `email_already_registered`; `429` `signup_ip_rate_limited` |
102
+ | **Spec** | `spec/requests/command_tower/auth/register_spec.rb` |
103
+
104
+ `user` fields: `id`, `email`, `username`, `firstName`, `lastName`, `emailValidated`, `roles`.
105
+
106
+ ### `POST /auth/plain-text/login`
107
+
108
+ | | |
109
+ |--|--|
110
+ | **Auth** | Public |
111
+ | **Gate** | `config.login.plain_text.enable?` (else route absent → **404**) |
112
+ | **Body** | `identifier`, `password` (snake_case) |
113
+ | **Success** | **201** — `data`: `{ user, token, tokenExpiresAt }` |
114
+ | **Errors** | `401` `invalid_credentials` |
115
+ | **Spec** | `spec/requests/command_tower/auth/plain_text/login_spec.rb` |
116
+
117
+ ### `POST /auth/logout`
118
+
119
+ | | |
120
+ |--|--|
121
+ | **Auth** | Public |
122
+ | **Body** | none |
123
+ | **Success** | **200** — `data`: `{ message: "logged_out" }` (clears auth cookie when cookie mode is enabled) |
124
+ | **Spec** | `spec/requests/command_tower/auth/logout_spec.rb` |
125
+
126
+ ### `GET /auth/session`
127
+
128
+ | | |
129
+ |--|--|
130
+ | **Auth** | `authenticate_request!` + `authorize_request!` |
131
+ | **Success** | **200** — `data`: `{ user, tokenExpiresAt }` |
132
+ | **Errors** | `401`; `403`; `412` `email_verification_required` when email verification gate applies |
133
+ | **Spec** | `spec/requests/command_tower/auth/session_spec.rb` |
134
+
135
+ ### `POST /auth/signup-session`
136
+
137
+ | | |
138
+ |--|--|
139
+ | **Auth** | Public |
140
+ | **Body** | none |
141
+ | **Success** | **201** — `data`: `{ signupSessionToken, expiresAt }` (ISO8601) |
142
+ | **Errors** | `429` `signup_ip_rate_limited` |
143
+ | **Spec** | `spec/requests/command_tower/auth/signup_session_spec.rb` |
144
+
145
+ ### `GET /auth/identity-policy`
146
+
147
+ | | |
148
+ |--|--|
149
+ | **Auth** | Public |
150
+ | **Success** | **200** — `data`: password / email / username / verificationCode / phoneVerificationCode policy objects (`minLength`, `maxLength`, `pattern`, …) |
151
+ | **Spec** | `spec/requests/command_tower/auth/identity_policy_spec.rb` |
152
+
153
+ ### `GET /auth/email/availability`
154
+
155
+ | | |
156
+ |--|--|
157
+ | **Auth** | Signup session (`Authorization: Signup …`) |
158
+ | **Gate** | `config.signup_session.email_availability?` |
159
+ | **Query** | `email` (required) |
160
+ | **Success** | **200** — `data`: `{ valid, available, message }` |
161
+ | **Errors** | `401` `signup_session_missing` / `signup_session_invalid` / `signup_session_expired`; `422`; `429` |
162
+ | **Spec** | `spec/requests/command_tower/auth/email_availability_spec.rb` |
163
+
164
+ ### `GET /auth/username/availability`
165
+
166
+ | | |
167
+ |--|--|
168
+ | **Auth** | Signup session |
169
+ | **Gate** | `config.username.realtime_username_check?` |
170
+ | **Query** | `username` (required) |
171
+ | **Success** | **200** — `data`: `{ valid, available, message }` |
172
+ | **Errors** | Same signup-session family as email availability |
173
+ | **Spec** | `spec/requests/command_tower/auth/username_availability_spec.rb` |
174
+
175
+ ### `POST /auth/email-verification/send`
176
+
177
+ | | |
178
+ |--|--|
179
+ | **Auth** | `authenticate_request!(bypass_email_validation: true)` + authorize |
180
+ | **Gate** | `config.login.plain_text.email_verify?` |
181
+ | **Body** | none |
182
+ | **Success** | **201** when sent / **200** when already verified — `data`: `{ message }` |
183
+ | **Errors** | `401`/`403`; `502` `verification_send_failed` |
184
+ | **Spec** | `spec/requests/command_tower/auth/email_verification_spec.rb` |
185
+
186
+ ### `POST /auth/email-verification/verify`
187
+
188
+ | | |
189
+ |--|--|
190
+ | **Auth** | Same as send (bypass email validation) |
191
+ | **Gate** | Same |
192
+ | **Body** | `code` |
193
+ | **Success** | **201** `"Successfully verified email"` / **200** already verified — `data`: `{ message }` |
194
+ | **Errors** | `422` `verification_code_invalid` |
195
+ | **Spec** | `spec/requests/command_tower/auth/email_verification_spec.rb` |
196
+
197
+ Workflow detail: [email_verification_workflow.md](email_verification_workflow.md).
198
+
199
+ ### `POST /auth/password-recovery-session`
200
+
201
+ | | |
202
+ |--|--|
203
+ | **Auth** | Public (always drawn; not gated by password_reset) |
204
+ | **Success** | **201** — `data`: `{ recoverySessionToken, expiresAt }` |
205
+ | **Errors** | `429` `password_recovery_ip_rate_limited` |
206
+ | **Spec** | `spec/requests/command_tower/auth/password_recovery_session_spec.rb` |
207
+
208
+ ### `POST /auth/password-reset/send`
209
+
210
+ | | |
211
+ |--|--|
212
+ | **Auth** | Password recovery session (`Authorization: Recovery …`) |
213
+ | **Gate** | `config.login.plain_text.password_reset?` |
214
+ | **Body** | `email` |
215
+ | **Success** | **200** — generic message (does not reveal whether the account exists) |
216
+ | **Errors** | `401` `password_recovery_session_*`; `422`; `429`; `503` `password_reset_unavailable` |
217
+ | **Specs** | `password_reset_send_spec.rb`, `password_reset_cross_token_spec.rb` |
218
+
219
+ ### `POST /auth/password-reset/validate`
220
+
221
+ | | |
222
+ |--|--|
223
+ | **Auth** | Public |
224
+ | **Gate** | `password_reset?` |
225
+ | **Body** | `token` (required); `email` optional unless config requires it |
226
+ | **Success** | **200** — `data`: `{ valid: true, expiresAt? }` |
227
+ | **Errors** | `401` `password_reset_invalid_token`; `422` |
228
+ | **Spec** | `spec/requests/command_tower/auth/password_reset_validate_and_reset_spec.rb` |
229
+
230
+ ### `POST /auth/password-reset/reset`
231
+
232
+ | | |
233
+ |--|--|
234
+ | **Auth** | Public |
235
+ | **Gate** | `password_reset?` |
236
+ | **Body** | `token`, `password`, `passwordConfirmation` or `password_confirmation`; optional `email` |
237
+ | **Success** | **200** — `data`: `{ message: "Password has been successfully reset" }` |
238
+ | **Errors** | `401` `password_reset_invalid_token`; `422` |
239
+ | **Spec** | `spec/requests/command_tower/auth/password_reset_validate_and_reset_spec.rb` |
240
+
241
+ Workflow detail: [password_reset_workflow.md](password_reset_workflow.md).
242
+
243
+ ---
244
+
245
+ ## Me and profile
246
+
247
+ ### `GET /me`
248
+
249
+ | | |
250
+ |--|--|
251
+ | **Auth** | authenticate + authorize |
252
+ | **Success** | **200** — account payload including `id`, `firstName`, `lastName`, `fullName`, `username`, `email`, `emailValidated`, `phoneNumber`, `phoneNumberValidated`, `roles`, `createdAt`, `capabilities` |
253
+ | **Spec** | `spec/requests/command_tower/me_spec.rb` |
254
+
255
+ `capabilities` keys (each `{ enabled: boolean }`): `editName`, `editUsername`, `changeEmail`, `changePassword`, `editPhone`, `editPushover`, `logoutAllDevices`, `verifyEmail`.
256
+
257
+ ### `GET /profile`
258
+
259
+ | | |
260
+ |--|--|
261
+ | **Auth** | authenticate + authorize |
262
+ | **Success** | **200** — UserSerializer only (`id`, `email`, `username`, `firstName`, `lastName`, `emailValidated`, `roles`) |
263
+ | **Spec** | `spec/requests/command_tower/profile_spec.rb` |
264
+
265
+ ### `PATCH /me/name`
266
+
267
+ | | |
268
+ |--|--|
269
+ | **Auth** | authenticate + authorize |
270
+ | **Body** | `firstName`/`first_name`, `lastName`/`last_name` |
271
+ | **Success** | **200** — AccountSerializer (same shape family as `GET /me`) |
272
+ | **Errors** | `422` `validation_failed` |
273
+ | **Spec** | `spec/requests/command_tower/me/name_spec.rb` |
274
+
275
+ Name-only. There is no engine HTTP for email/username self-modify or verifier rotation except via password change (and host ops).
276
+
277
+ ### `PATCH /me/password`
278
+
279
+ | | |
280
+ |--|--|
281
+ | **Auth** | authenticate + authorize |
282
+ | **Body** | `currentPassword`/`current_password`, `password`, `passwordConfirmation`/`password_confirmation` |
283
+ | **Success** | **200** — `data`: `{ message: "Password updated successfully." }` |
284
+ | **Errors** | `422` validation / wrong current password |
285
+ | **Spec** | `spec/requests/command_tower/me/password_spec.rb` |
286
+
287
+ Rotates `verifier_token` (invalidates outstanding sessions). See [change_password_workflow.md](change_password_workflow.md) and [sensitive_routes.md](sensitive_routes.md).
288
+
289
+ ---
290
+
291
+ ## Me Inbox
292
+
293
+ All inbox routes: authenticate + authorize. Host must map RBAC entities for inbox controller actions (see dummy host `rails_app/config/rbac_groups.yml`).
294
+
295
+ Pagination for list: query `limit` (default **50**, max **100**), `offset` (default **0**), `scope` (`inbox` \| `archived`, default `inbox`). List `meta`: `{ limit, offset, totalCount }`. See [pagination.md](pagination.md).
296
+
297
+ | Method | Path | Notes |
298
+ |--------|------|--------|
299
+ | `GET` | `/me/inbox` | List — `data` array of items; pagination meta |
300
+ | `GET` | `/me/inbox/:id` | Detail (+ `body`, `metadata`, `notificationTypeKey`) |
301
+ | `POST` | `/me/inbox/:id/open` | Detail |
302
+ | `PATCH` | `/me/inbox/:id/archive` | Item |
303
+ | `DELETE` | `/me/inbox/:id` | `data: null` |
304
+ | `GET` | `/me/inbox/unread-count` | `{ count }` |
305
+ | `POST` | `/me/inbox/bulk/read` | Body `ids` (1–100 ints) → `{ ids, count, changedCount }` |
306
+ | `POST` | `/me/inbox/bulk/unread` | same |
307
+ | `POST` | `/me/inbox/bulk/archive` | same |
308
+ | `POST` | `/me/inbox/bulk/restore` | same |
309
+ | `POST` | `/me/inbox/bulk/delete` | same |
310
+
311
+ **List item fields:** `id`, `title`, `status`, `read`, `viewedAt`, `createdAt`, `updatedAt`.
312
+
313
+ **Errors:** `401` / `403` / `422`; show/open may return `404` `not_found`.
314
+
315
+ **Specs:** `spec/requests/command_tower/me/inbox_spec.rb`, `me/inbox_bulk_spec.rb`.
316
+
317
+ ---
318
+
319
+ ## Preferences
320
+
321
+ | | Show | Update |
322
+ |--|------|--------|
323
+ | **Path** | `GET /me/preferences` | `PATCH /me/preferences/:notification_type_key` |
324
+ | **Auth** | authenticate + authorize | same |
325
+ | **Body** | none | `preferences: { inboxEnabled?, channels?: { <channelKey>: bool } }` (preference keys camelCase; unknown keys rejected) |
326
+ | **Success** | `data`: `{ categories: [...] }` | `data`: `{ notification: ... }` |
327
+ | **Errors** | `401`/`403`; update `404` unknown type; `422` invalid prefs |
328
+ | **Spec** | `spec/requests/command_tower/me/preferences_spec.rb` | |
329
+
330
+ Category / notification serializers expose catalog fields (`key`, `label`, `description`, `order`, channel availability, `preferences: { inboxEnabled, channels, storedOverridePresent }`, …).
331
+
332
+ ---
333
+
334
+ ## Phone
335
+
336
+ Routes are **always drawn**. Product SMS readiness is workflow-gated as **503** `sms_capability_unavailable`.
337
+
338
+ | Method | Path | Body | Success | Spec |
339
+ |--------|------|------|---------|------|
340
+ | `PATCH` | `/me/phone` | `phoneNumber`/`phone_number` | AccountSerializer | `me/phone_spec.rb` |
341
+ | `DELETE` | `/me/phone` | — | AccountSerializer | same |
342
+ | `POST` | `/me/phone/verification` | — | `{ codeLength, expiresAt, resendAvailableAt?, phoneNumber }` | `me/phone_verification_spec.rb` |
343
+ | `POST` | `/me/phone/verification/verify` | `code` (or nested `phone_verification.code`) | AccountSerializer | same |
344
+
345
+ Other errors include `422` (`phone_missing`, `phone_verification_code_invalid`, …), `429` `phone_verification_throttled` (meta may include `resendAvailableAt`), `502` `phone_verification_send_failed`.
346
+
347
+ ---
348
+
349
+ ## Pushover
350
+
351
+ Routes are **always drawn**. Product readiness is workflow-gated as **503** `pushover_capability_unavailable`.
352
+
353
+ | Method | Path | Body | Notes |
354
+ |--------|------|------|--------|
355
+ | `GET` | `/me/pushover` | — | Configured or unconfigured view |
356
+ | `POST` | `/me/pushover` | `userKey`/`user_key`, `applicationToken`/`application_token` | Create |
357
+ | `PATCH` / `PUT` | `/me/pushover` | same | Replace |
358
+ | `DELETE` | `/me/pushover` | — | Unconfigured view |
359
+ | `POST` | `/me/pushover/verification` | — | Verify configured credentials |
360
+
361
+ **Configured fields include:** `configured`, `id`, `channelKey`, `lifecycleState`, `verificationState`, `maskedDisplayValue`, `credentialsConfigured`, `verifiedAt`, `createdAt`, `updatedAt`, `actions: { canCreate, canVerify, canReplace, canRemove }`.
362
+
363
+ **Spec:** `spec/requests/command_tower/me/pushover_spec.rb`.
364
+
365
+ Errors include `422` (`pushover_already_configured`, `pushover_not_configured`, …), `502` `pushover_provider_unavailable`, `503`.
366
+
367
+ ---
368
+
369
+ ## Admin messaging
370
+
371
+ ### `POST /admin/messaging/announcements`
372
+
373
+ | | |
374
+ |--|--|
375
+ | **Auth** | authenticate + authorize (RBAC entity `admin_messaging_announcements`) |
376
+ | **Success** | **202** |
377
+ | **Spec** | `spec/requests/command_tower/admin/messaging/announcements_spec.rb` |
378
+
379
+ **Body** (camel preferred; snake via underscore fallback): `title`, `body`, `campaignIdentity` (required), `audience` (`user_ids` \| `all_users`), `userIds` (required when `user_ids`), `notificationTypeKey` (default `"promotional_announcement"`), `executionMode` (`async` \| `sync`, default `async`), `metadata` (optional).
380
+
381
+ **Async response:** `mode`, `requested`, `campaignIdentity`, `enqueued`, `enqueueFailed`.
382
+
383
+ **Sync response:** `mode`, `requested`, `campaignIdentity`, `accepted`, `failed`, `skipped`, `failures: [{ userId, errorCode }]`.
384
+
385
+ Engine admin HTTP is **announcements only**. There is no `/admin` user list, modify, role assign, or impersonate surface.
386
+
387
+ ---
388
+
389
+ ## Non-HTTP emit APIs
390
+
391
+ Hosts call these from **product workflows** (not as HTTP on the engine):
392
+
393
+ | Service | Required kwargs |
394
+ |---------|-----------------|
395
+ | `CommandTower::Services::Messaging::Communications::Produce` | `user`, `notification_type_key`, `host_event_identity`, `title`, `body`, `platform_enabled_channels`; optional `metadata` |
396
+ | `CommandTower::Services::Messaging::Communications::ProduceMany` | `user_ids`, `notification_type_key`, `campaign_identity`, `title`, `body`, `platform_enabled_channels`; optional `metadata`, `execution_mode` (`:async` default, `:sync` capped at 25) |
397
+
398
+ Details: [messaging_integration_guide.md](messaging_integration_guide.md).
399
+
400
+ ---
401
+
402
+ ## RBAC overview
403
+
404
+ Engine defaults (`lib/command_tower/authorization/default.yml`):
405
+
406
+ - Group `owner` — all entities
407
+ - Group `admin` — entity `admin_messaging_announcements` on `AnnouncementsController#create`
408
+
409
+ Hosts **must** supply `rbac_groups.yml` entities for Me / Auth / session surfaces (fail-closed). The dummy host file `rails_app/config/rbac_groups.yml` shows a `member` mapping pattern.
410
+
411
+ Configure via `CommandTower.configure { |c| c.authorization.rbac_group_path = ... }`. Deep guide: [authentication_authorization_guide.md](authentication_authorization_guide.md). Quick start: [authorization.md](authorization.md).
412
+
413
+ ---
414
+
415
+ ## Feature gates
416
+
417
+ When a gate is off, the route is **not drawn** → **404**.
418
+
419
+ | Routes | Config |
420
+ |--------|--------|
421
+ | `POST /auth/plain-text/login` | `login.plain_text.enable?` |
422
+ | `GET /auth/email/availability` | `signup_session.email_availability?` |
423
+ | `GET /auth/username/availability` | `username.realtime_username_check?` |
424
+ | Email verification send/verify | `login.plain_text.email_verify?` |
425
+ | Password reset send/validate/reset | `login.plain_text.password_reset?` |
426
+
427
+ Always drawn (not route-gated): register, logout, session, signup-session, identity-policy, password-recovery-session, Me/profile/inbox/preferences/phone/pushover, admin announcements. Phone/Pushover use **503** capability errors when product adapters are unavailable.
428
+
429
+ ---
430
+
431
+ ## Related
432
+
433
+ - [Authentication](authentication.md) / [Authorization](authorization.md)
434
+ - [Cookie authentication](cookie_authentication_guide.md)
435
+ - [Sensitive changes](sensitive_routes.md)
436
+ - [Pagination](pagination.md)
437
+ - [Messaging](messaging_integration_guide.md)
438
+ - [README](../README.md)
@@ -0,0 +1,30 @@
1
+ # CommandTower Architecture (host-facing)
2
+
3
+ CommandTower is a mountable Rails engine. Host applications consume platform capabilities through engine HTTP, configuration, and shared factories. Product behavior stays in the host.
4
+
5
+ ## Layer map
6
+
7
+ ```text
8
+ Controller / Job → Workflow → Shared Sequences / Services → Models & Clients
9
+ ```
10
+
11
+ | Layer | Owns | Does not own |
12
+ |-------|------|--------------|
13
+ | Controller | Transport, auth preconditions, deserialization, **one** workflow, envelope render | Business rules, persistence, external I/O |
14
+ | Workflow | Orchestration of one business action | Capability implementation details |
15
+ | Shared sequence | Reusable orchestration fragments | External entry points |
16
+ | Service | One business capability (`ServiceBase` / ApplicationService) | Workflows, transport |
17
+ | Model | Persistence and data validation | Orchestration, external I/O |
18
+ | Client | External system I/O | Business orchestration |
19
+
20
+ **Hard rule:** meaningful business behavior enters through a workflow—never directly from a controller into a service for a full business action.
21
+
22
+ ## Where to read more
23
+
24
+ - Long-form rationale (workspace): `artifacts/rails_architecture_handbook.md`
25
+ - Condensed enforcement (workspace): `.cursor/rules/rails-architecture.mdc`
26
+ - Service capability base: [ServiceBase README](../app/services/command_tower/README.md)
27
+ - Install and host ownership: [Initializing](initializing.md)
28
+ - Host extension boundaries: [Extending](extending.md)
29
+
30
+ Back to [README](../README.md).
@@ -0,0 +1,37 @@
1
+ # Authentication
2
+
3
+ Authentication establishes **who** is calling. Failed authentication returns `401`.
4
+
5
+ ## Engine controllers
6
+
7
+ Modern engine controllers use the authentication boundary:
8
+
9
+ ```ruby
10
+ # via AuthenticationBoundary
11
+ before_action :authenticate_request!
12
+ ```
13
+
14
+ Token extraction checks `Authorization: Bearer {token}` first, then an HttpOnly cookie when cookie auth is enabled. Failures use the application envelope (`{ data, meta, errors }`).
15
+
16
+ ## Host controllers (provisional)
17
+
18
+ ```ruby
19
+ before_action :authenticate_user!
20
+ # current_user is set when authentication succeeds
21
+ ```
22
+
23
+ Failure JSON may still use older `Schema::Error` shapes — not identical to the engine envelope. See the deep guide.
24
+
25
+ Configure secrets and JWT options during install — see [Initializing](initializing.md).
26
+
27
+ ## Where to go next
28
+
29
+ | Need | Guide |
30
+ |------|-------|
31
+ | Full authn + RBAC design | [Authentication & authorization guide](authentication_authorization_guide.md) |
32
+ | Cookie / CORS / CSRF | [Cookie authentication](cookie_authentication_guide.md) |
33
+ | HTTP contracts | [API reference](api_reference.md) |
34
+ | Session invalidation | [Sensitive changes](sensitive_routes.md) |
35
+ | Authorization (RBAC) | [Authorization](authorization.md) |
36
+
37
+ Back to [README](../README.md).
@@ -0,0 +1,150 @@
1
+ # Authentication & Authorization Guide
2
+
3
+ Deep guide for JWT authn, session boundaries, RBAC, and how Engine vs host helpers differ.
4
+
5
+ Quick starts: [authentication.md](authentication.md), [authorization.md](authorization.md). HTTP catalog: [api_reference.md](api_reference.md). Cookies: [cookie_authentication_guide.md](cookie_authentication_guide.md).
6
+
7
+ ## Authn vs authz
8
+
9
+ | Concern | Meaning | Typical failure |
10
+ |---------|---------|-----------------|
11
+ | **Authentication** | Who is calling | `401` |
12
+ | **Authorization** | Are they allowed to perform this action | `403` |
13
+
14
+ Keep them separate. Authenticate first; authorize after `current_user` is established.
15
+
16
+ ## Engine HTTP boundaries
17
+
18
+ Modern engine controllers use concerns:
19
+
20
+ - `CommandTower::Auth::AuthenticationBoundary` → `authenticate_request!`
21
+ - `CommandTower::Auth::AuthorizationBoundary` → `authorize_request!`
22
+
23
+ Failures render the application **envelope** (`{ data, meta, errors }`) via `render_application_result`.
24
+
25
+ Email verification controllers call `authenticate_request!(bypass_email_validation: true)` so unverified users can complete verification.
26
+
27
+ ### Host provisional helpers
28
+
29
+ `CommandTower::ApplicationController` still exposes:
30
+
31
+ ```ruby
32
+ before_action :authenticate_user!
33
+ before_action :authorize_user!
34
+ ```
35
+
36
+ Hosts may use these on host controllers. Failure shapes can still use older `Schema::Error::*` JSON — **not** identical to the engine envelope. Prefer aligning new host JSON APIs to the envelope yourself, or call into workflows that return `WorkflowResult`.
37
+
38
+ ## Token transport
39
+
40
+ 1. **Bearer** — `Authorization: Bearer <jwt>` (primary)
41
+ 2. **Cookie** — optional HttpOnly JWT when `config.jwt.cookie.enabled` (see cookie guide)
42
+ 3. **Signup session** — `Authorization: Signup <token>` for availability checks
43
+ 4. **Password recovery session** — `Authorization: Recovery <token>` for password-reset send
44
+
45
+ Default JWT TTL: **7 days** (`config.jwt.ttl`).
46
+
47
+ ### Verifier token
48
+
49
+ JWTs embed a `verifier_token` claim bound to `User#verifier_token`. Rotation invalidates outstanding tokens. See [sensitive_routes.md](sensitive_routes.md).
50
+
51
+ Authenticated password change (`PATCH /me/password`) rotates the verifier. Browser `POST /auth/logout` clears cookie transport only.
52
+
53
+ ## Primary auth HTTP flows
54
+
55
+ Paths are engine-relative. Full request/response shapes: [api_reference.md](api_reference.md).
56
+
57
+ | Flow | Endpoints |
58
+ |------|-----------|
59
+ | Register | `POST /auth/register` |
60
+ | Login | `POST /auth/plain-text/login` (gate: `login.plain_text.enable?`) |
61
+ | Session | `GET /auth/session` |
62
+ | Logout | `POST /auth/logout` |
63
+ | Current account | `GET /me`, `GET /profile` |
64
+ | Name | `PATCH /me/name` |
65
+ | Password change | `PATCH /me/password` |
66
+ | Identity policy | `GET /auth/identity-policy` |
67
+ | Signup session | `POST /auth/signup-session` → availability GETs |
68
+ | Email verification | `POST /auth/email-verification/{send,verify}` |
69
+ | Password recovery | `POST /auth/password-recovery-session` → `POST /auth/password-reset/{send,validate,reset}` |
70
+
71
+ There is **no** `GET /user`, `POST /user/modify`, `POST /auth/login` (without `plain-text`), or `POST /auth/password/change` on the modern engine.
72
+
73
+ ## Session boundaries
74
+
75
+ ### Signup session
76
+
77
+ 1. `POST /auth/signup-session` → `signupSessionToken`
78
+ 2. Call gated availability endpoints with `Authorization: Signup …`
79
+
80
+ Used by email/username availability. Missing/invalid/expired session → `401` with `signup_session_*` codes.
81
+
82
+ ### Password recovery session
83
+
84
+ 1. `POST /auth/password-recovery-session` → `recoverySessionToken`
85
+ 2. `POST /auth/password-reset/send` requires `Authorization: Recovery …`
86
+
87
+ Validate/reset use the emailed reset token in the body (public). See [password_reset_workflow.md](password_reset_workflow.md).
88
+
89
+ ## RBAC
90
+
91
+ ### Engine defaults
92
+
93
+ `lib/command_tower/authorization/default.yml`:
94
+
95
+ - **`owner`** — `entities: true` (full access)
96
+ - **`admin`** — entity `admin_messaging_announcements` on `Admin::Messaging::AnnouncementsController#create`
97
+
98
+ There are no engine default roles named `admin-read-only`, `admin-without-impersonation`, or impersonation APIs.
99
+
100
+ ### Host RBAC file (required for Me/Auth)
101
+
102
+ `AuthorizeRequest` fails closed when controller actions lack entity mappings. Hosts must supply RBAC YAML (default path `config/rbac_groups.yml`):
103
+
104
+ ```ruby
105
+ CommandTower.configure do |c|
106
+ c.authorization.rbac_group_path = Rails.root.join("config/rbac_groups.yml")
107
+ end
108
+ ```
109
+
110
+ Dummy host example: `rails_app/config/rbac_groups.yml` — defines a **`member`** group and entities for session, me, profile, inbox, preferences, phone, pushover, email verification. Do not redefine groups that already exist in `default.yml` (for example `admin`); add entities and attach them carefully.
111
+
112
+ Admin announcements: assign users the `admin` role (or another role that includes `admin_messaging_announcements`).
113
+
114
+ ### Host controller recipe
115
+
116
+ ```ruby
117
+ class Host::ThingsController < ApplicationController
118
+ before_action :authenticate_user!
119
+ before_action :authorize_user!
120
+
121
+ def show
122
+ # Prefer one workflow + envelope-compatible render for new APIs
123
+ end
124
+ end
125
+ ```
126
+
127
+ Map host controllers to RBAC entities the same way engine controllers are mapped in the dummy host file.
128
+
129
+ ## Engine admin HTTP
130
+
131
+ Only:
132
+
133
+ ```http
134
+ POST /admin/messaging/announcements
135
+ ```
136
+
137
+ No SchemaHelper admin user list, attribute modify, role assign, or impersonate routes. User administration is host/ops (`command_tower:users:create`, host tooling, etc.).
138
+
139
+ ## Email verification gate
140
+
141
+ When email verification is required, authenticated requests may receive **412** `email_verification_required` until verified. Verification endpoints bypass that gate. See [email_verification_workflow.md](email_verification_workflow.md).
142
+
143
+ ## Related
144
+
145
+ - [API reference](api_reference.md)
146
+ - [Cookie authentication](cookie_authentication_guide.md)
147
+ - [Sensitive changes](sensitive_routes.md)
148
+ - [Initializing](initializing.md)
149
+ - [Messaging](messaging_integration_guide.md)
150
+ - [README](../README.md)
@@ -0,0 +1,34 @@
1
+ # Authorization (RBAC)
2
+
3
+ Authorization establishes **permission** after authentication. Failed authorization returns `403`.
4
+
5
+ Host `rbac_groups.yml` is a **required integration step** (Step 4) — see [Host integration](host_integration_guide.md#step-4--host-rbac-required). Without it, authenticated Me/Auth calls fail closed.
6
+
7
+ ## Quick usage (host provisional)
8
+
9
+ ```ruby
10
+ # Order matters: authorize depends on current_user from authentication
11
+ before_action :authenticate_user!
12
+ before_action :authorize_user!
13
+ ```
14
+
15
+ Engine controllers use `authorize_request!` instead (AuthorizationBoundary) and return the application envelope on failure.
16
+
17
+ ## Roles and host mapping
18
+
19
+ Engine defaults in `lib/command_tower/authorization/default.yml`:
20
+
21
+ - `owner` — full access (`entities: true`)
22
+ - `admin` — `admin_messaging_announcements` only
23
+
24
+ Hosts **must** map Me/Auth controller actions in host YAML (`CommandTower.config.authorization.rbac_group_path`, default `config/rbac_groups.yml`) or authorization fails closed. See dummy host `rails_app/config/rbac_groups.yml` (`member` entities).
25
+
26
+ ## Where to go next
27
+
28
+ | Need | Guide |
29
+ |------|-------|
30
+ | Full RBAC entities, roles, failure shapes | [Authentication & authorization guide](authentication_authorization_guide.md) |
31
+ | Authentication | [Authentication](authentication.md) |
32
+ | Install / config | [Initializing](initializing.md) |
33
+
34
+ Back to [README](../README.md).