command_tower 0.4.0 → 0.11.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 (722) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +106 -29
  3. data/app/auth/command_tower/auth/auth_context.rb +42 -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/application_controller.rb +27 -0
  7. data/app/controllers/command_tower/admin/audit/events_controller.rb +61 -0
  8. data/app/controllers/command_tower/admin/messaging/announcements_controller.rb +27 -0
  9. data/app/controllers/command_tower/admin/users/identities_controller.rb +77 -0
  10. data/app/controllers/command_tower/admin/users/impersonation_sessions_controller.rb +33 -0
  11. data/app/controllers/command_tower/admin/users/roles_controller.rb +40 -0
  12. data/app/controllers/command_tower/admin/users_controller.rb +46 -0
  13. data/app/controllers/command_tower/admin/workspace_controller.rb +15 -0
  14. data/app/controllers/command_tower/application_controller.rb +97 -15
  15. data/app/controllers/command_tower/auth/email/availability_controller.rb +35 -0
  16. data/app/controllers/command_tower/auth/email_verification/send_controller.rb +24 -0
  17. data/app/controllers/command_tower/auth/email_verification/verify_controller.rb +41 -0
  18. data/app/controllers/command_tower/auth/identity_policy_controller.rb +14 -0
  19. data/app/controllers/command_tower/auth/impersonation_session_controller.rb +27 -0
  20. data/app/controllers/command_tower/auth/logout_controller.rb +16 -0
  21. data/app/controllers/command_tower/auth/password_recovery_session/create_controller.rb +17 -0
  22. data/app/controllers/command_tower/auth/password_reset/reset_controller.rb +35 -0
  23. data/app/controllers/command_tower/auth/password_reset/send_controller.rb +40 -0
  24. data/app/controllers/command_tower/auth/password_reset/validate_controller.rb +35 -0
  25. data/app/controllers/command_tower/auth/plain_text/login_controller.rb +22 -0
  26. data/app/controllers/command_tower/auth/principal_capabilities_controller.rb +19 -0
  27. data/app/controllers/command_tower/auth/register_controller.rb +33 -0
  28. data/app/controllers/command_tower/auth/session_controller.rb +21 -0
  29. data/app/controllers/command_tower/auth/signup_session/create_controller.rb +17 -0
  30. data/app/controllers/command_tower/auth/username/availability_controller.rb +35 -0
  31. data/app/controllers/command_tower/me/audit_events_controller.rb +59 -0
  32. data/app/controllers/command_tower/me/inbox_controller.rb +85 -0
  33. data/app/controllers/command_tower/me/name_controller.rb +37 -0
  34. data/app/controllers/command_tower/me/password_controller.rb +37 -0
  35. data/app/controllers/command_tower/me/phone_controller.rb +44 -0
  36. data/app/controllers/command_tower/me/phone_verification/verifications_controller.rb +23 -0
  37. data/app/controllers/command_tower/me/phone_verification/verify_controller.rb +34 -0
  38. data/app/controllers/command_tower/me/preferences_controller.rb +46 -0
  39. data/app/controllers/command_tower/me/pushover/verifications_controller.rb +23 -0
  40. data/app/controllers/command_tower/me/pushover_controller.rb +66 -0
  41. data/app/controllers/command_tower/me_controller.rb +19 -0
  42. data/app/controllers/command_tower/profile_controller.rb +19 -0
  43. data/app/controllers/concerns/command_tower/api/application_response_renderer.rb +87 -0
  44. data/app/controllers/concerns/command_tower/api/deserializer_failure_details.rb +22 -0
  45. data/app/controllers/concerns/command_tower/auth/authentication_boundary.rb +35 -0
  46. data/app/controllers/concerns/command_tower/auth/authorization_boundary.rb +26 -0
  47. data/app/controllers/concerns/command_tower/auth/invalid_credentials_deserializer_renderer.rb +23 -0
  48. data/app/controllers/concerns/command_tower/auth/password_recovery_session_boundary.rb +29 -0
  49. data/app/controllers/concerns/command_tower/auth/signup_session_boundary.rb +29 -0
  50. data/app/controllers/concerns/command_tower/execution/http_boundary.rb +65 -0
  51. data/app/deserializers/command_tower/deserializers/admin/messaging/create_announcement_deserializer.rb +106 -0
  52. data/app/deserializers/command_tower/deserializers/admin/scope_parameter.rb +49 -0
  53. data/app/deserializers/command_tower/deserializers/admin/users.rb +218 -0
  54. data/app/deserializers/command_tower/deserializers/application_deserializer.rb +148 -0
  55. data/app/deserializers/command_tower/deserializers/audit/events.rb +224 -0
  56. data/app/deserializers/command_tower/deserializers/auth/email_availability_deserializer.rb +19 -0
  57. data/app/deserializers/command_tower/deserializers/auth/email_verification/verify_deserializer.rb +21 -0
  58. data/app/deserializers/command_tower/deserializers/auth/password_reset/reset_deserializer.rb +31 -0
  59. data/app/deserializers/command_tower/deserializers/auth/password_reset/send_deserializer.rb +21 -0
  60. data/app/deserializers/command_tower/deserializers/auth/password_reset/validate_deserializer.rb +23 -0
  61. data/app/deserializers/command_tower/deserializers/auth/plain_text/login_deserializer.rb +24 -0
  62. data/app/deserializers/command_tower/deserializers/auth/register_deserializer.rb +42 -0
  63. data/app/deserializers/command_tower/deserializers/auth/username_availability_deserializer.rb +19 -0
  64. data/app/deserializers/command_tower/deserializers/clients/errors.rb +45 -0
  65. data/app/deserializers/command_tower/deserializers/clients/missing.rb +10 -0
  66. data/app/deserializers/command_tower/deserializers/clients/payload.rb +21 -0
  67. data/app/deserializers/command_tower/deserializers/clients/result.rb +122 -0
  68. data/app/deserializers/command_tower/deserializers/clients/types.rb +219 -0
  69. data/app/deserializers/command_tower/deserializers/coercion_result.rb +35 -0
  70. data/app/deserializers/command_tower/deserializers/failure.rb +14 -0
  71. data/app/deserializers/command_tower/deserializers/me/change_password_deserializer.rb +41 -0
  72. data/app/deserializers/command_tower/deserializers/me/phone_verification/verify_deserializer.rb +29 -0
  73. data/app/deserializers/command_tower/deserializers/me/pushover/credentials_deserializer.rb +31 -0
  74. data/app/deserializers/command_tower/deserializers/me/update_name_deserializer.rb +22 -0
  75. data/app/deserializers/command_tower/deserializers/me/update_phone_deserializer.rb +22 -0
  76. data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +77 -0
  77. data/app/deserializers/command_tower/deserializers/messaging/preferences/show_deserializer.rb +17 -0
  78. data/app/deserializers/command_tower/deserializers/messaging/preferences/update_deserializer.rb +97 -0
  79. data/app/errors/command_tower/errors/account/phone_already_verified_error.rb +17 -0
  80. data/app/errors/command_tower/errors/account/phone_missing_error.rb +17 -0
  81. data/app/errors/command_tower/errors/account/phone_verification_code_invalid_error.rb +17 -0
  82. data/app/errors/command_tower/errors/account/phone_verification_expired_error.rb +17 -0
  83. data/app/errors/command_tower/errors/account/phone_verification_send_failed_error.rb +25 -0
  84. data/app/errors/command_tower/errors/account/phone_verification_stale_error.rb +17 -0
  85. data/app/errors/command_tower/errors/account/phone_verification_throttled_error.rb +24 -0
  86. data/app/errors/command_tower/errors/account/pushover_already_configured_error.rb +21 -0
  87. data/app/errors/command_tower/errors/account/pushover_capability_unavailable_error.rb +21 -0
  88. data/app/errors/command_tower/errors/account/pushover_not_configured_error.rb +21 -0
  89. data/app/errors/command_tower/errors/account/pushover_provider_unavailable_error.rb +21 -0
  90. data/app/errors/command_tower/errors/account/pushover_verification_failed_error.rb +27 -0
  91. data/app/errors/command_tower/errors/account/sms_capability_unavailable_error.rb +21 -0
  92. data/app/errors/command_tower/errors/application_error.rb +27 -0
  93. data/app/errors/command_tower/errors/auth/admin_unavailable_during_impersonation_error.rb +17 -0
  94. data/app/errors/command_tower/errors/auth/csrf_mismatch_error.rb +17 -0
  95. data/app/errors/command_tower/errors/auth/csrf_missing_error.rb +17 -0
  96. data/app/errors/command_tower/errors/auth/default_membership_assignment_error.rb +17 -0
  97. data/app/errors/command_tower/errors/auth/email_already_registered_error.rb +17 -0
  98. data/app/errors/command_tower/errors/auth/email_verification_required_error.rb +21 -0
  99. data/app/errors/command_tower/errors/auth/impersonation_session_expired_error.rb +17 -0
  100. data/app/errors/command_tower/errors/auth/impersonation_session_missing_error.rb +21 -0
  101. data/app/errors/command_tower/errors/auth/invalid_credentials_error.rb +21 -0
  102. data/app/errors/command_tower/errors/auth/nested_impersonation_error.rb +17 -0
  103. data/app/errors/command_tower/errors/auth/password_recovery_ip_rate_limit_error.rb +25 -0
  104. data/app/errors/command_tower/errors/auth/password_recovery_session_expired_error.rb +21 -0
  105. data/app/errors/command_tower/errors/auth/password_recovery_session_invalid_error.rb +21 -0
  106. data/app/errors/command_tower/errors/auth/password_recovery_session_missing_error.rb +21 -0
  107. data/app/errors/command_tower/errors/auth/password_recovery_session_rate_limit_error.rb +25 -0
  108. data/app/errors/command_tower/errors/auth/password_reset_invalid_token_error.rb +21 -0
  109. data/app/errors/command_tower/errors/auth/password_reset_unavailable_error.rb +21 -0
  110. data/app/errors/command_tower/errors/auth/self_impersonation_error.rb +21 -0
  111. data/app/errors/command_tower/errors/auth/signup_ip_rate_limit_error.rb +25 -0
  112. data/app/errors/command_tower/errors/auth/signup_session_expired_error.rb +17 -0
  113. data/app/errors/command_tower/errors/auth/signup_session_invalid_error.rb +17 -0
  114. data/app/errors/command_tower/errors/auth/signup_session_missing_error.rb +17 -0
  115. data/app/errors/command_tower/errors/auth/signup_session_rate_limit_error.rb +25 -0
  116. data/app/errors/command_tower/errors/auth/verification_code_invalid_error.rb +17 -0
  117. data/app/errors/command_tower/errors/auth/verification_send_failed_error.rb +25 -0
  118. data/app/errors/command_tower/errors/continuation_exhausted_error.rb +25 -0
  119. data/app/errors/command_tower/errors/forbidden_error.rb +19 -0
  120. data/app/errors/command_tower/errors/internal_error.rb +19 -0
  121. data/app/errors/command_tower/errors/messaging/accept_rejected_error.rb +21 -0
  122. data/app/errors/command_tower/errors/messaging/idempotency_conflict_error.rb +21 -0
  123. data/app/errors/command_tower/errors/messaging/recipient_unresolved_error.rb +21 -0
  124. data/app/errors/command_tower/errors/not_found_error.rb +19 -0
  125. data/app/errors/command_tower/errors/unauthorized_error.rb +19 -0
  126. data/app/errors/command_tower/errors/validation_error.rb +15 -0
  127. data/app/jobs/command_tower/application_job.rb +7 -0
  128. data/app/jobs/command_tower/execution/job_boundary.rb +19 -0
  129. data/app/jobs/command_tower/messaging/channel_delivery_execution_job.rb +18 -0
  130. data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +24 -0
  131. data/app/jobs/command_tower/messaging/execution_recovery_job.rb +13 -0
  132. data/app/jobs/command_tower/messaging/handoff_job.rb +15 -0
  133. data/app/jobs/command_tower/messaging/handoff_recovery_job.rb +13 -0
  134. data/app/mailers/command_tower/application_mailer.rb +11 -1
  135. data/app/mailers/command_tower/email_verification_mailer.rb +5 -3
  136. data/app/mailers/command_tower/messaging/channel_mailer.rb +24 -0
  137. data/app/mailers/command_tower/password_reset_mailer.rb +18 -0
  138. data/app/models/command_tower/audit/event.rb +36 -0
  139. data/app/models/command_tower/impersonation/session.rb +51 -0
  140. data/app/models/command_tower/messaging/channel_delivery.rb +45 -0
  141. data/app/models/command_tower/messaging/communication.rb +60 -0
  142. data/app/models/command_tower/messaging/delivery_attempt.rb +28 -0
  143. data/app/models/command_tower/messaging/destination_plan.rb +17 -0
  144. data/app/models/command_tower/messaging/endpoint.rb +98 -0
  145. data/app/models/command_tower/messaging/endpoint_pushover_credential.rb +18 -0
  146. data/app/models/command_tower/messaging/inbox_item.rb +52 -0
  147. data/app/models/command_tower/messaging/notification_preference.rb +21 -0
  148. data/app/models/user.rb +4 -1
  149. data/app/serializers/command_tower/serializers/admin/messaging/announcement_response_serializer.rb +41 -0
  150. data/app/serializers/command_tower/serializers/admin/users.rb +69 -0
  151. data/app/serializers/command_tower/serializers/admin/workspace/manifest_serializer.rb +58 -0
  152. data/app/serializers/command_tower/serializers/application/envelope_serializer.rb +25 -0
  153. data/app/serializers/command_tower/serializers/application/error_entry_serializer.rb +18 -0
  154. data/app/serializers/command_tower/serializers/application_serializer.rb +26 -0
  155. data/app/serializers/command_tower/serializers/audit/events/filter_options_serializer.rb +44 -0
  156. data/app/serializers/command_tower/serializers/audit/events.rb +42 -0
  157. data/app/serializers/command_tower/serializers/auth/email_availability_serializer.rb +17 -0
  158. data/app/serializers/command_tower/serializers/auth/email_verification/message_response_serializer.rb +15 -0
  159. data/app/serializers/command_tower/serializers/auth/identity_policy_serializer.rb +36 -0
  160. data/app/serializers/command_tower/serializers/auth/login_response_serializer.rb +17 -0
  161. data/app/serializers/command_tower/serializers/auth/logout_response_serializer.rb +13 -0
  162. data/app/serializers/command_tower/serializers/auth/password_recovery_session_response_serializer.rb +16 -0
  163. data/app/serializers/command_tower/serializers/auth/password_reset/message_response_serializer.rb +15 -0
  164. data/app/serializers/command_tower/serializers/auth/password_reset/validate_response_serializer.rb +22 -0
  165. data/app/serializers/command_tower/serializers/auth/principal_capabilities_serializer.rb +15 -0
  166. data/app/serializers/command_tower/serializers/auth/register_response_serializer.rb +16 -0
  167. data/app/serializers/command_tower/serializers/auth/session_response_serializer.rb +40 -0
  168. data/app/serializers/command_tower/serializers/auth/signup_session_response_serializer.rb +16 -0
  169. data/app/serializers/command_tower/serializers/auth/user_serializer.rb +21 -0
  170. data/app/serializers/command_tower/serializers/auth/username_availability_serializer.rb +17 -0
  171. data/app/serializers/command_tower/serializers/impersonation/session_serializer.rb +19 -0
  172. data/app/serializers/command_tower/serializers/me/account_serializer.rb +45 -0
  173. data/app/serializers/command_tower/serializers/me/change_password_response_serializer.rb +13 -0
  174. data/app/serializers/command_tower/serializers/me/pushover_serializer.rb +59 -0
  175. data/app/serializers/command_tower/serializers/messaging/inbox.rb +45 -0
  176. data/app/serializers/command_tower/serializers/messaging/preferences/catalog_serializer.rb +17 -0
  177. data/app/serializers/command_tower/serializers/messaging/preferences/category_serializer.rb +23 -0
  178. data/app/serializers/command_tower/serializers/messaging/preferences/notification_serializer.rb +30 -0
  179. data/app/serializers/command_tower/serializers/profile/profile_serializer.rb +13 -0
  180. data/app/services/command_tower/README.md +24 -14
  181. data/app/services/command_tower/auth/request_context.rb +21 -0
  182. data/app/services/command_tower/authorize/validate.rb +58 -19
  183. data/app/services/command_tower/clients/client_base.rb +183 -0
  184. data/app/services/command_tower/clients/client_result.rb +45 -0
  185. data/app/services/command_tower/clients/const_resolution.rb +32 -0
  186. data/app/services/command_tower/clients/decoded_response.rb +16 -0
  187. data/app/services/command_tower/clients/endpoint_base.rb +242 -0
  188. data/app/services/command_tower/clients/endpoint_input.rb +18 -0
  189. data/app/services/command_tower/clients/errors/authentication_error.rb +26 -0
  190. data/app/services/command_tower/clients/errors/configuration_error.rb +10 -0
  191. data/app/services/command_tower/clients/errors/deserialization_error.rb +26 -0
  192. data/app/services/command_tower/clients/errors/discovery_error.rb +10 -0
  193. data/app/services/command_tower/clients/errors/upstream_error.rb +26 -0
  194. data/app/services/command_tower/clients/invocation.rb +61 -0
  195. data/app/services/command_tower/clients/namespace_proxy.rb +75 -0
  196. data/app/services/command_tower/clients/scoped_client.rb +88 -0
  197. data/app/services/command_tower/clients/serialized_request.rb +17 -0
  198. data/app/services/command_tower/clients/transport/error.rb +18 -0
  199. data/app/services/command_tower/clients/transport/faraday_adapter.rb +129 -0
  200. data/app/services/command_tower/clients/transport/faraday_config.rb +45 -0
  201. data/app/services/command_tower/clients/transport/request.rb +50 -0
  202. data/app/services/command_tower/clients/transport/response.rb +22 -0
  203. data/app/services/command_tower/clients/url.rb +56 -0
  204. data/app/services/command_tower/identity/phone/normalizer.rb +41 -0
  205. data/app/services/command_tower/identity/phone_verification/sms_configuration.rb +58 -0
  206. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/fake_adapter.rb +35 -0
  207. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/log_adapter.rb +34 -0
  208. data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/twilio_adapter.rb +60 -0
  209. data/app/services/command_tower/identity/phone_verification/sms_transport.rb +42 -0
  210. data/app/services/command_tower/jwt/authenticate_user.rb +67 -34
  211. data/app/services/command_tower/jwt/authentication_outcome.rb +37 -0
  212. data/app/services/command_tower/jwt/decode.rb +27 -8
  213. data/app/services/command_tower/jwt/encode.rb +4 -6
  214. data/app/services/command_tower/jwt/login_create.rb +10 -8
  215. data/app/services/command_tower/login_strategy/plain_text/email_verification/required.rb +11 -9
  216. data/app/services/command_tower/messaging/accept/channel_delivery_result.rb +13 -0
  217. data/app/services/command_tower/messaging/accept/coordinator.rb +166 -0
  218. data/app/services/command_tower/messaging/accept/error.rb +9 -0
  219. data/app/services/command_tower/messaging/accept/excluded_destination.rb +24 -0
  220. data/app/services/command_tower/messaging/accept/handoff_scheduler.rb +40 -0
  221. data/app/services/command_tower/messaging/accept/idempotency_conflict_error.rb +9 -0
  222. data/app/services/command_tower/messaging/accept/illegal_override_error.rb +9 -0
  223. data/app/services/command_tower/messaging/accept/impossible_mandatory_plan_error.rb +9 -0
  224. data/app/services/command_tower/messaging/accept/invalid_preference_error.rb +9 -0
  225. data/app/services/command_tower/messaging/accept/invariant_error.rb +9 -0
  226. data/app/services/command_tower/messaging/accept/operation_logger.rb +97 -0
  227. data/app/services/command_tower/messaging/accept/persistence_error.rb +9 -0
  228. data/app/services/command_tower/messaging/accept/persister.rb +90 -0
  229. data/app/services/command_tower/messaging/accept/request_normalizer.rb +100 -0
  230. data/app/services/command_tower/messaging/accept/result.rb +99 -0
  231. data/app/services/command_tower/messaging/accept/unknown_type_error.rb +9 -0
  232. data/app/services/command_tower/messaging/accept/validation_error.rb +9 -0
  233. data/app/services/command_tower/messaging/channel_detectors.rb +52 -0
  234. data/app/services/command_tower/messaging/channels/definition.rb +24 -0
  235. data/app/services/command_tower/messaging/channels/error.rb +9 -0
  236. data/app/services/command_tower/messaging/channels/unknown_channel_error.rb +9 -0
  237. data/app/services/command_tower/messaging/channels.rb +85 -0
  238. data/app/services/command_tower/messaging/contract/communications.rb +29 -0
  239. data/app/services/command_tower/messaging/contract/error.rb +9 -0
  240. data/app/services/command_tower/messaging/contract/internal/finder.rb +43 -0
  241. data/app/services/command_tower/messaging/contract/invariant_error.rb +9 -0
  242. data/app/services/command_tower/messaging/contract/mappers/channel_delivery_mapper.rb +53 -0
  243. data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +61 -0
  244. data/app/services/command_tower/messaging/contract/mappers/delivery_attempt_mapper.rb +27 -0
  245. data/app/services/command_tower/messaging/contract/mappers/destination_plan_mapper.rb +17 -0
  246. data/app/services/command_tower/messaging/contract/mappers/inbox_item_mapper.rb +17 -0
  247. data/app/services/command_tower/messaging/contract/not_found_error.rb +9 -0
  248. data/app/services/command_tower/messaging/contract/observability/correlation.rb +19 -0
  249. data/app/services/command_tower/messaging/contract/observability/operation_logger.rb +101 -0
  250. data/app/services/command_tower/messaging/contract/observability/publisher.rb +44 -0
  251. data/app/services/command_tower/messaging/contract/requests/find_communication.rb +21 -0
  252. data/app/services/command_tower/messaging/contract/results/channel_delivery_result.rb +25 -0
  253. data/app/services/command_tower/messaging/contract/results/communication_result.rb +22 -0
  254. data/app/services/command_tower/messaging/contract/results/delivery_attempt_result.rb +21 -0
  255. data/app/services/command_tower/messaging/contract/results/destination_plan_result.rb +11 -0
  256. data/app/services/command_tower/messaging/contract/results/inbox_item_result.rb +11 -0
  257. data/app/services/command_tower/messaging/contract/validation_error.rb +9 -0
  258. data/app/services/command_tower/messaging/delivery_retry_policy.rb +30 -0
  259. data/app/services/command_tower/messaging/endpoints/begin_verification.rb +13 -0
  260. data/app/services/command_tower/messaging/endpoints/channel_gate.rb +39 -0
  261. data/app/services/command_tower/messaging/endpoints/conflict_error.rb +9 -0
  262. data/app/services/command_tower/messaging/endpoints/create.rb +181 -0
  263. data/app/services/command_tower/messaging/endpoints/error.rb +9 -0
  264. data/app/services/command_tower/messaging/endpoints/fingerprinter.rb +50 -0
  265. data/app/services/command_tower/messaging/endpoints/invalid_transition_error.rb +9 -0
  266. data/app/services/command_tower/messaging/endpoints/lifecycle.rb +37 -0
  267. data/app/services/command_tower/messaging/endpoints/mark_invalid.rb +32 -0
  268. data/app/services/command_tower/messaging/endpoints/mark_verification_failed.rb +13 -0
  269. data/app/services/command_tower/messaging/endpoints/mark_verified.rb +13 -0
  270. data/app/services/command_tower/messaging/endpoints/not_found_error.rb +9 -0
  271. data/app/services/command_tower/messaging/endpoints/persistence.rb +91 -0
  272. data/app/services/command_tower/messaging/endpoints/pushover/verify.rb +101 -0
  273. data/app/services/command_tower/messaging/endpoints/query.rb +28 -0
  274. data/app/services/command_tower/messaging/endpoints/replace.rb +181 -0
  275. data/app/services/command_tower/messaging/endpoints/reset_verification.rb +13 -0
  276. data/app/services/command_tower/messaging/endpoints/revoke.rb +40 -0
  277. data/app/services/command_tower/messaging/endpoints/safe_view.rb +50 -0
  278. data/app/services/command_tower/messaging/endpoints/secret_box.rb +86 -0
  279. data/app/services/command_tower/messaging/endpoints/secret_reader.rb +72 -0
  280. data/app/services/command_tower/messaging/endpoints/validation_error.rb +9 -0
  281. data/app/services/command_tower/messaging/endpoints/validators/push.rb +24 -0
  282. data/app/services/command_tower/messaging/endpoints/validators/pushover.rb +68 -0
  283. data/app/services/command_tower/messaging/endpoints/validators.rb +36 -0
  284. data/app/services/command_tower/messaging/endpoints/verification.rb +39 -0
  285. data/app/services/command_tower/messaging/endpoints/verification_failed_error.rb +18 -0
  286. data/app/services/command_tower/messaging/endpoints/verification_transition.rb +20 -0
  287. data/app/services/command_tower/messaging/endpoints.rb +55 -0
  288. data/app/services/command_tower/messaging/execution/adapter_request.rb +45 -0
  289. data/app/services/command_tower/messaging/execution/adapter_result.rb +81 -0
  290. data/app/services/command_tower/messaging/execution/adapters/email/adapter.rb +78 -0
  291. data/app/services/command_tower/messaging/execution/adapters/email/configuration.rb +55 -0
  292. data/app/services/command_tower/messaging/execution/adapters/fake_adapter.rb +34 -0
  293. data/app/services/command_tower/messaging/execution/adapters/pushover/adapter.rb +107 -0
  294. data/app/services/command_tower/messaging/execution/adapters/pushover/configuration.rb +30 -0
  295. data/app/services/command_tower/messaging/execution/adapters/sms/adapter.rb +90 -0
  296. data/app/services/command_tower/messaging/execution/adapters/sms/configuration.rb +58 -0
  297. data/app/services/command_tower/messaging/execution/adapters/sms/log_adapter.rb +34 -0
  298. data/app/services/command_tower/messaging/execution/adapters/sms/twilio_http_client.rb +70 -0
  299. data/app/services/command_tower/messaging/execution/adapters/unconfigured_adapter.rb +20 -0
  300. data/app/services/command_tower/messaging/execution/attempt_ledger.rb +146 -0
  301. data/app/services/command_tower/messaging/execution/claim.rb +35 -0
  302. data/app/services/command_tower/messaging/execution/invalid_adapter_contract_error.rb +9 -0
  303. data/app/services/command_tower/messaging/execution/operation_logger.rb +237 -0
  304. data/app/services/command_tower/messaging/execution/readiness_terminal_mapping.rb +37 -0
  305. data/app/services/command_tower/messaging/execution/recovery.rb +78 -0
  306. data/app/services/command_tower/messaging/execution/resolve_recipient_address.rb +58 -0
  307. data/app/services/command_tower/messaging/execution/revalidate_readiness.rb +73 -0
  308. data/app/services/command_tower/messaging/execution/scheduler.rb +38 -0
  309. data/app/services/command_tower/messaging/execution/select_adapter.rb +63 -0
  310. data/app/services/command_tower/messaging/handoff/advance_communication.rb +39 -0
  311. data/app/services/command_tower/messaging/handoff/operation_logger.rb +78 -0
  312. data/app/services/command_tower/messaging/handoff/recovery.rb +26 -0
  313. data/app/services/command_tower/messaging/inbox/bulk_result.rb +19 -0
  314. data/app/services/command_tower/messaging/inbox/error.rb +9 -0
  315. data/app/services/command_tower/messaging/inbox/invalid_bulk_items_error.rb +19 -0
  316. data/app/services/command_tower/messaging/inbox/invariant_error.rb +9 -0
  317. data/app/services/command_tower/messaging/inbox/item_result.rb +64 -0
  318. data/app/services/command_tower/messaging/inbox/list_result.rb +18 -0
  319. data/app/services/command_tower/messaging/inbox/mutator.rb +252 -0
  320. data/app/services/command_tower/messaging/inbox/not_found_error.rb +9 -0
  321. data/app/services/command_tower/messaging/inbox/operation_logger.rb +37 -0
  322. data/app/services/command_tower/messaging/inbox/reader.rb +117 -0
  323. data/app/services/command_tower/messaging/inbox/scope.rb +24 -0
  324. data/app/services/command_tower/messaging/inbox/unread_count_result.rb +16 -0
  325. data/app/services/command_tower/messaging/inbox/validation_error.rb +9 -0
  326. data/app/services/command_tower/messaging/inbox.rb +61 -0
  327. data/app/services/command_tower/messaging/notification_types/catalog_category.rb +19 -0
  328. data/app/services/command_tower/messaging/notification_types/declaration.rb +96 -0
  329. data/app/services/command_tower/messaging/notification_types/declaration_validator.rb +152 -0
  330. data/app/services/command_tower/messaging/notification_types/duplicate_type_error.rb +9 -0
  331. data/app/services/command_tower/messaging/notification_types/error.rb +9 -0
  332. data/app/services/command_tower/messaging/notification_types/invalid_declaration_error.rb +9 -0
  333. data/app/services/command_tower/messaging/notification_types/not_found_error.rb +9 -0
  334. data/app/services/command_tower/messaging/notification_types/registry.rb +103 -0
  335. data/app/services/command_tower/messaging/notification_types/sealed_registry_error.rb +9 -0
  336. data/app/services/command_tower/messaging/notification_types.rb +37 -0
  337. data/app/services/command_tower/messaging/planner/destination_plan.rb +41 -0
  338. data/app/services/command_tower/messaging/planner/error.rb +9 -0
  339. data/app/services/command_tower/messaging/planner/excluded_destination.rb +28 -0
  340. data/app/services/command_tower/messaging/planner/illegal_override_error.rb +9 -0
  341. data/app/services/command_tower/messaging/planner/impossible_mandatory_plan_error.rb +9 -0
  342. data/app/services/command_tower/messaging/planner/invalid_evaluation_error.rb +9 -0
  343. data/app/services/command_tower/messaging/planner/message_overrides.rb +33 -0
  344. data/app/services/command_tower/messaging/planner/operation_logger.rb +28 -0
  345. data/app/services/command_tower/messaging/planner/planner.rb +261 -0
  346. data/app/services/command_tower/messaging/planner/unknown_type_error.rb +9 -0
  347. data/app/services/command_tower/messaging/planner.rb +25 -0
  348. data/app/services/command_tower/messaging/preferences/catalog.rb +96 -0
  349. data/app/services/command_tower/messaging/preferences/catalog_category_result.rb +19 -0
  350. data/app/services/command_tower/messaging/preferences/catalog_notification_result.rb +55 -0
  351. data/app/services/command_tower/messaging/preferences/catalog_result.rb +13 -0
  352. data/app/services/command_tower/messaging/preferences/error.rb +9 -0
  353. data/app/services/command_tower/messaging/preferences/evaluation_result.rb +61 -0
  354. data/app/services/command_tower/messaging/preferences/evaluator.rb +194 -0
  355. data/app/services/command_tower/messaging/preferences/invalid_preference_state_error.rb +9 -0
  356. data/app/services/command_tower/messaging/preferences/invalid_preference_write_error.rb +10 -0
  357. data/app/services/command_tower/messaging/preferences/not_settings_visible_error.rb +10 -0
  358. data/app/services/command_tower/messaging/preferences/preference_state.rb +61 -0
  359. data/app/services/command_tower/messaging/preferences/reason_classes.rb +17 -0
  360. data/app/services/command_tower/messaging/preferences/resolve.rb +50 -0
  361. data/app/services/command_tower/messaging/preferences/store.rb +126 -0
  362. data/app/services/command_tower/messaging/preferences/store_error.rb +10 -0
  363. data/app/services/command_tower/messaging/preferences/suppressed_destination.rb +24 -0
  364. data/app/services/command_tower/messaging/preferences/unknown_type_error.rb +9 -0
  365. data/app/services/command_tower/messaging/preferences/update.rb +248 -0
  366. data/app/services/command_tower/messaging/preferences.rb +68 -0
  367. data/app/services/command_tower/messaging/pushover/adapters/disabled_adapter.rb +33 -0
  368. data/app/services/command_tower/messaging/pushover/adapters/fake_adapter.rb +90 -0
  369. data/app/services/command_tower/messaging/pushover/adapters/http_adapter.rb +145 -0
  370. data/app/services/command_tower/messaging/pushover/adapters/log_adapter.rb +52 -0
  371. data/app/services/command_tower/messaging/pushover/result.rb +34 -0
  372. data/app/services/command_tower/messaging/pushover/transport.rb +64 -0
  373. data/app/services/command_tower/messaging/recipient_readiness/channel_result.rb +55 -0
  374. data/app/services/command_tower/messaging/recipient_readiness/error.rb +9 -0
  375. data/app/services/command_tower/messaging/recipient_readiness/evaluate.rb +319 -0
  376. data/app/services/command_tower/messaging/recipient_readiness/identity_facts.rb +54 -0
  377. data/app/services/command_tower/messaging/recipient_readiness/reason_codes.rb +33 -0
  378. data/app/services/command_tower/messaging/recipient_readiness/recipient_not_found_error.rb +9 -0
  379. data/app/services/command_tower/messaging/recipient_readiness/recipient_result.rb +38 -0
  380. data/app/services/command_tower/messaging/recipient_readiness/unknown_channel_error.rb +9 -0
  381. data/app/services/command_tower/messaging/recipient_readiness.rb +26 -0
  382. data/app/services/command_tower/messaging/rendering/channel_renderer.rb +127 -0
  383. data/app/services/command_tower/messaging/rendering/render_error.rb +24 -0
  384. data/app/services/command_tower/messaging/rendering/rendered_payload.rb +44 -0
  385. data/app/services/command_tower/messaging/rendering/rendered_pushover_payload.rb +43 -0
  386. data/app/services/command_tower/messaging/rendering/rendered_sms_payload.rb +40 -0
  387. data/app/services/command_tower/messaging.rb +35 -0
  388. data/app/services/command_tower/secrets/cleanse.rb +19 -5
  389. data/app/services/command_tower/secrets/generate.rb +62 -27
  390. data/app/services/command_tower/secrets/verify.rb +46 -12
  391. data/app/services/command_tower/secrets.rb +2 -0
  392. data/app/services/command_tower/service_base.rb +30 -43
  393. data/app/services/command_tower/service_logging.rb +2 -36
  394. data/app/services/command_tower/services/account/clear_phone.rb +33 -0
  395. data/app/services/command_tower/services/account/phone_verification/send.rb +114 -0
  396. data/app/services/command_tower/services/account/phone_verification/verify.rb +89 -0
  397. data/app/services/command_tower/services/account/pushover/create.rb +28 -0
  398. data/app/services/command_tower/services/account/pushover/ct_support.rb +70 -0
  399. data/app/services/command_tower/services/account/pushover/destroy.rb +30 -0
  400. data/app/services/command_tower/services/account/pushover/replace.rb +28 -0
  401. data/app/services/command_tower/services/account/pushover/show.rb +19 -0
  402. data/app/services/command_tower/services/account/pushover/verify.rb +30 -0
  403. data/app/services/command_tower/services/account/update_phone.rb +58 -0
  404. data/app/services/command_tower/services/admin/users.rb +377 -0
  405. data/app/services/command_tower/services/admin/workspace/manifest.rb +49 -0
  406. data/app/services/command_tower/services/application_service.rb +58 -0
  407. data/app/services/command_tower/services/audit/events/filter_options.rb +75 -0
  408. data/app/services/command_tower/services/audit/events.rb +199 -0
  409. data/app/services/command_tower/services/auth/assign_default_membership_role.rb +44 -0
  410. data/app/services/command_tower/services/auth/authenticate_session.rb +132 -0
  411. data/app/services/command_tower/services/auth/authorize_request.rb +34 -0
  412. data/app/services/command_tower/services/auth/client_ip_resolver.rb +13 -0
  413. data/app/services/command_tower/services/auth/email_availability.rb +42 -0
  414. data/app/services/command_tower/services/auth/email_verification/send.rb +65 -0
  415. data/app/services/command_tower/services/auth/email_verification/verify.rb +53 -0
  416. data/app/services/command_tower/services/auth/identity_policy.rb +60 -0
  417. data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_send.rb +48 -0
  418. data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_token_issue.rb +35 -0
  419. data/app/services/command_tower/services/auth/password_recovery/rate_limits/enforcement.rb +53 -0
  420. data/app/services/command_tower/services/auth/password_recovery_session/create.rb +26 -0
  421. data/app/services/command_tower/services/auth/password_recovery_session/decode.rb +29 -0
  422. data/app/services/command_tower/services/auth/password_recovery_session/encode.rb +31 -0
  423. data/app/services/command_tower/services/auth/password_recovery_session/validate.rb +58 -0
  424. data/app/services/command_tower/services/auth/password_reset/reset.rb +75 -0
  425. data/app/services/command_tower/services/auth/password_reset/send.rb +77 -0
  426. data/app/services/command_tower/services/auth/password_reset/token_verification.rb +68 -0
  427. data/app/services/command_tower/services/auth/password_reset/validate.rb +29 -0
  428. data/app/services/command_tower/services/auth/plain_text/login.rb +52 -0
  429. data/app/services/command_tower/services/auth/principal_capabilities/project.rb +48 -0
  430. data/app/services/command_tower/services/auth/register.rb +77 -0
  431. data/app/services/command_tower/services/auth/signup_rate_limits/check_availability.rb +52 -0
  432. data/app/services/command_tower/services/auth/signup_rate_limits/check_register.rb +24 -0
  433. data/app/services/command_tower/services/auth/signup_rate_limits/check_token_issue.rb +33 -0
  434. data/app/services/command_tower/services/auth/signup_rate_limits/enforcement.rb +50 -0
  435. data/app/services/command_tower/services/auth/signup_session/create.rb +26 -0
  436. data/app/services/command_tower/services/auth/signup_session/decode.rb +29 -0
  437. data/app/services/command_tower/services/auth/signup_session/encode.rb +31 -0
  438. data/app/services/command_tower/services/auth/signup_session/validate.rb +58 -0
  439. data/app/services/command_tower/services/auth/username_availability.rb +28 -0
  440. data/app/services/command_tower/services/impersonation/create.rb +29 -0
  441. data/app/services/command_tower/services/impersonation/end.rb +28 -0
  442. data/app/services/command_tower/services/impersonation/record_activity.rb +30 -0
  443. data/app/services/command_tower/services/impersonation/terminate_open_sessions.rb +51 -0
  444. data/app/services/command_tower/services/me/capabilities.rb +43 -0
  445. data/app/services/command_tower/services/me/change_password.rb +96 -0
  446. data/app/services/command_tower/services/me/pushover_product_gate.rb +18 -0
  447. data/app/services/command_tower/services/me/sms_product_gate.rb +19 -0
  448. data/app/services/command_tower/services/me/update_name.rb +58 -0
  449. data/app/services/command_tower/services/messaging/communications/produce.rb +68 -0
  450. data/app/services/command_tower/services/messaging/communications/produce_many.rb +154 -0
  451. data/app/services/command_tower/services/messaging/inbox.rb +129 -0
  452. data/app/services/command_tower/services/messaging/preferences/platform_enabled_channels.rb +20 -0
  453. data/app/services/command_tower/services/messaging/preferences/show.rb +36 -0
  454. data/app/services/command_tower/services/messaging/preferences/update.rb +43 -0
  455. data/app/services/command_tower/services/messaging/recipients.rb +20 -0
  456. data/app/services/command_tower/services/rate_limits/check.rb +31 -0
  457. data/app/services/command_tower/services/service_result.rb +73 -0
  458. data/app/services/command_tower/user_attributes/mutate.rb +154 -0
  459. data/app/services/command_tower/username/available.rb +24 -11
  460. data/app/shared_sequences/command_tower/shared_sequences/admin/users/resolve_scoped_user.rb +42 -0
  461. data/app/views/command_tower/email_verification_mailer/verify_email.html.erb +70 -22
  462. data/app/views/command_tower/messaging/rendering/email.html.erb +28 -0
  463. data/app/views/command_tower/messaging/rendering/email.text.erb +7 -0
  464. data/app/views/command_tower/messaging/rendering/pushover.text.erb +1 -0
  465. data/app/views/command_tower/messaging/rendering/sms.text.erb +1 -0
  466. data/app/views/command_tower/password_reset_mailer/reset_password.html.erb +107 -0
  467. data/app/views/layouts/mailer.html.erb +13 -0
  468. data/app/views/layouts/mailer.text.erb +1 -0
  469. data/app/workflows/command_tower/transactional.rb +65 -0
  470. data/app/workflows/command_tower/workflows/admin/messaging/create_announcement_workflow.rb +73 -0
  471. data/app/workflows/command_tower/workflows/admin/scope_resolution.rb +17 -0
  472. data/app/workflows/command_tower/workflows/admin/users/error_mapping.rb +26 -0
  473. data/app/workflows/command_tower/workflows/admin/users/identity_mutation.rb +30 -0
  474. data/app/workflows/command_tower/workflows/admin/users/list_assignable_roles_workflow.rb +30 -0
  475. data/app/workflows/command_tower/workflows/admin/users/list_workflow.rb +54 -0
  476. data/app/workflows/command_tower/workflows/admin/users/set_email_validated_workflow.rb +45 -0
  477. data/app/workflows/command_tower/workflows/admin/users/show_workflow.rb +49 -0
  478. data/app/workflows/command_tower/workflows/admin/users/update_email_workflow.rb +45 -0
  479. data/app/workflows/command_tower/workflows/admin/users/update_name_workflow.rb +49 -0
  480. data/app/workflows/command_tower/workflows/admin/users/update_roles_workflow.rb +61 -0
  481. data/app/workflows/command_tower/workflows/admin/users/update_username_workflow.rb +45 -0
  482. data/app/workflows/command_tower/workflows/admin/workspace/manifest_workflow.rb +25 -0
  483. data/app/workflows/command_tower/workflows/application_workflow.rb +207 -0
  484. data/app/workflows/command_tower/workflows/audit/error_mapping.rb +24 -0
  485. data/app/workflows/command_tower/workflows/audit/events/filter_options_for_admin_workflow.rb +34 -0
  486. data/app/workflows/command_tower/workflows/audit/events/filter_options_for_user_workflow.rb +34 -0
  487. data/app/workflows/command_tower/workflows/audit/events/list_for_admin_workflow.rb +74 -0
  488. data/app/workflows/command_tower/workflows/audit/events/list_for_user_workflow.rb +45 -0
  489. data/app/workflows/command_tower/workflows/audit/events/show_for_admin_workflow.rb +54 -0
  490. data/app/workflows/command_tower/workflows/audit/events/show_for_user_workflow.rb +42 -0
  491. data/app/workflows/command_tower/workflows/auth/authenticate_request_workflow.rb +41 -0
  492. data/app/workflows/command_tower/workflows/auth/authentication_response_effects.rb +25 -0
  493. data/app/workflows/command_tower/workflows/auth/authorize_request_workflow.rb +28 -0
  494. data/app/workflows/command_tower/workflows/auth/email_availability_workflow.rb +39 -0
  495. data/app/workflows/command_tower/workflows/auth/email_verification/send_workflow.rb +32 -0
  496. data/app/workflows/command_tower/workflows/auth/email_verification/verify_workflow.rb +35 -0
  497. data/app/workflows/command_tower/workflows/auth/identity_error_status.rb +36 -0
  498. data/app/workflows/command_tower/workflows/auth/identity_policy_workflow.rb +26 -0
  499. data/app/workflows/command_tower/workflows/auth/logout_workflow.rb +59 -0
  500. data/app/workflows/command_tower/workflows/auth/password_recovery_session/authenticate_workflow.rb +49 -0
  501. data/app/workflows/command_tower/workflows/auth/password_recovery_session/create_workflow.rb +39 -0
  502. data/app/workflows/command_tower/workflows/auth/password_reset/reset_workflow.rb +40 -0
  503. data/app/workflows/command_tower/workflows/auth/password_reset/send_workflow.rb +45 -0
  504. data/app/workflows/command_tower/workflows/auth/password_reset/validate_workflow.rb +40 -0
  505. data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +48 -0
  506. data/app/workflows/command_tower/workflows/auth/principal_capabilities/show_workflow.rb +27 -0
  507. data/app/workflows/command_tower/workflows/auth/register_workflow.rb +115 -0
  508. data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +58 -0
  509. data/app/workflows/command_tower/workflows/auth/session_error_status.rb +31 -0
  510. data/app/workflows/command_tower/workflows/auth/signup_error_status.rb +31 -0
  511. data/app/workflows/command_tower/workflows/auth/signup_session/authenticate_workflow.rb +49 -0
  512. data/app/workflows/command_tower/workflows/auth/signup_session/create_workflow.rb +37 -0
  513. data/app/workflows/command_tower/workflows/auth/username_availability_workflow.rb +39 -0
  514. data/app/workflows/command_tower/workflows/impersonation/error_mapping.rb +27 -0
  515. data/app/workflows/command_tower/workflows/impersonation/start_workflow.rb +97 -0
  516. data/app/workflows/command_tower/workflows/impersonation/stop_workflow.rb +66 -0
  517. data/app/workflows/command_tower/workflows/me/change_password_workflow.rb +34 -0
  518. data/app/workflows/command_tower/workflows/me/clear_phone_workflow.rb +52 -0
  519. data/app/workflows/command_tower/workflows/me/error_mapping.rb +46 -0
  520. data/app/workflows/command_tower/workflows/me/error_status.rb +28 -0
  521. data/app/workflows/command_tower/workflows/me/phone_verification/send_workflow.rb +65 -0
  522. data/app/workflows/command_tower/workflows/me/phone_verification/verify_workflow.rb +57 -0
  523. data/app/workflows/command_tower/workflows/me/pushover/create_workflow.rb +38 -0
  524. data/app/workflows/command_tower/workflows/me/pushover/destroy_workflow.rb +34 -0
  525. data/app/workflows/command_tower/workflows/me/pushover/replace_workflow.rb +38 -0
  526. data/app/workflows/command_tower/workflows/me/pushover/show_workflow.rb +34 -0
  527. data/app/workflows/command_tower/workflows/me/pushover/verify_workflow.rb +34 -0
  528. data/app/workflows/command_tower/workflows/me/pushover/workflow_support.rb +31 -0
  529. data/app/workflows/command_tower/workflows/me/show_workflow.rb +33 -0
  530. data/app/workflows/command_tower/workflows/me/update_name_workflow.rb +49 -0
  531. data/app/workflows/command_tower/workflows/me/update_phone_workflow.rb +55 -0
  532. data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +60 -0
  533. data/app/workflows/command_tower/workflows/messaging/error_mapping.rb +26 -0
  534. data/app/workflows/command_tower/workflows/messaging/execution/deliver_workflow.rb +234 -0
  535. data/app/workflows/command_tower/workflows/messaging/handoff/process_workflow.rb +90 -0
  536. data/app/workflows/command_tower/workflows/messaging/inbox.rb +97 -0
  537. data/app/workflows/command_tower/workflows/messaging/preferences/show_workflow.rb +31 -0
  538. data/app/workflows/command_tower/workflows/messaging/preferences/update_workflow.rb +37 -0
  539. data/app/workflows/command_tower/workflows/profile/show_workflow.rb +28 -0
  540. data/app/workflows/command_tower/workflows/workflow_result.rb +58 -0
  541. data/config/routes.rb +105 -37
  542. data/db/migrate/{20241117043720_create_command_tower_users.rb → 20260805000001_create_command_tower_users.rb} +7 -9
  543. data/db/migrate/{20241204065708_create_command_tower_user_secrets.rb → 20260805000002_create_command_tower_user_secrets.rb} +3 -0
  544. data/db/migrate/20260805000003_create_command_tower_messaging_core.rb +90 -0
  545. data/db/migrate/20260805000004_create_messaging_notification_preferences.rb +17 -0
  546. data/db/migrate/20260805000005_create_messaging_endpoints_and_pushover_credentials.rb +72 -0
  547. data/db/migrate/20260816000001_create_command_tower_audit_events.rb +45 -0
  548. data/db/migrate/20260817000001_add_scope_columns_to_command_tower_audit_events.rb +34 -0
  549. data/db/migrate/20260817000003_create_command_tower_impersonation_sessions.rb +26 -0
  550. data/docs/admin_workspace.md +158 -0
  551. data/docs/api_reference.md +599 -0
  552. data/docs/architecture.md +35 -0
  553. data/docs/audit.md +195 -0
  554. data/docs/authentication.md +51 -0
  555. data/docs/authentication_authorization_guide.md +154 -0
  556. data/docs/authorization.md +55 -0
  557. data/docs/change_password_workflow.md +67 -0
  558. data/docs/controllers.md +41 -0
  559. data/docs/cookie_authentication_guide.md +167 -0
  560. data/docs/email_verification_workflow.md +58 -0
  561. data/docs/eventing.md +179 -0
  562. data/docs/extending.md +136 -0
  563. data/docs/host_integration_guide.md +249 -0
  564. data/docs/initializing.md +169 -0
  565. data/docs/messaging_integration_guide.md +87 -0
  566. data/docs/models.md +55 -0
  567. data/docs/pagination.md +47 -0
  568. data/docs/password_reset_workflow.md +82 -0
  569. data/docs/principal_capabilities.md +89 -0
  570. data/docs/sensitive_routes.md +47 -0
  571. data/docs/testing.md +74 -0
  572. data/docs/upgrades/0.10.0.md +58 -0
  573. data/docs/upgrades/0.11.0.md +92 -0
  574. data/docs/upgrades/README.md +10 -0
  575. data/lib/command_tower/admin_scope/apply_audit_scoping.rb +45 -0
  576. data/lib/command_tower/admin_scope/apply_users_narrowing.rb +21 -0
  577. data/lib/command_tower/admin_scope/manifest_projection.rb +102 -0
  578. data/lib/command_tower/admin_scope/resolve.rb +31 -0
  579. data/lib/command_tower/admin_scope/scope_context.rb +7 -0
  580. data/lib/command_tower/admin_scope/scope_option.rb +7 -0
  581. data/lib/command_tower/admin_scope.rb +20 -0
  582. data/lib/command_tower/admin_workspace.rb +16 -0
  583. data/lib/command_tower/audit/attribution.rb +90 -0
  584. data/lib/command_tower/audit/emit.rb +113 -0
  585. data/lib/command_tower/audit/masking.rb +41 -0
  586. data/lib/command_tower/audit/payload.rb +128 -0
  587. data/lib/command_tower/audit/persistence/subscriber.rb +85 -0
  588. data/lib/command_tower/audit.rb +27 -0
  589. data/lib/command_tower/authorization/assignable_roles.rb +36 -0
  590. data/lib/command_tower/authorization/default.yml +101 -35
  591. data/lib/command_tower/authorization/effective_entity_grants.rb +55 -0
  592. data/lib/command_tower/authorization/entity.rb +15 -5
  593. data/lib/command_tower/authorization/role.rb +5 -4
  594. data/lib/command_tower/authorization.rb +71 -10
  595. data/lib/command_tower/configuration/admin_scope/config.rb +104 -0
  596. data/lib/command_tower/configuration/admin_scope/tool_registration.rb +28 -0
  597. data/lib/command_tower/configuration/application/config.rb +2 -2
  598. data/lib/command_tower/configuration/authorization/config.rb +6 -0
  599. data/lib/command_tower/configuration/base.rb +0 -1
  600. data/lib/command_tower/configuration/config.rb +51 -1
  601. data/lib/command_tower/configuration/credentials/config.rb +33 -0
  602. data/lib/command_tower/configuration/credentials/smtp.rb +33 -0
  603. data/lib/command_tower/configuration/credentials/twilio.rb +33 -0
  604. data/lib/command_tower/configuration/email/config.rb +1 -18
  605. data/lib/command_tower/configuration/identity/config.rb +19 -0
  606. data/lib/command_tower/configuration/identity/phone_verification.rb +54 -0
  607. data/lib/command_tower/configuration/impersonation/config.rb +38 -0
  608. data/lib/command_tower/configuration/jwt/config.rb +7 -0
  609. data/lib/command_tower/configuration/jwt/cookie/config.rb +64 -0
  610. data/lib/command_tower/configuration/jwt/cookie/csrf/config.rb +72 -0
  611. data/lib/command_tower/configuration/login/strategy/plain_text/config.rb +6 -0
  612. data/lib/command_tower/configuration/login/strategy/plain_text/email_verify.rb +5 -0
  613. data/lib/command_tower/configuration/login/strategy/plain_text/password_reset.rb +49 -0
  614. data/lib/command_tower/configuration/messaging/config.rb +60 -0
  615. data/lib/command_tower/configuration/messaging/pushover.rb +42 -0
  616. data/lib/command_tower/configuration/messaging/sms.rb +32 -0
  617. data/lib/command_tower/configuration/password_recovery_session/config.rb +61 -0
  618. data/lib/command_tower/configuration/password_recovery_session/rate_limits.rb +33 -0
  619. data/lib/command_tower/configuration/registry/admin_workspace/config.rb +199 -0
  620. data/lib/command_tower/configuration/registry/admin_workspace/tool_definition.rb +150 -0
  621. data/lib/command_tower/configuration/registry/audit/config.rb +403 -0
  622. data/lib/command_tower/configuration/registry/audit/event_definition.rb +155 -0
  623. data/lib/command_tower/configuration/registry/config.rb +34 -0
  624. data/lib/command_tower/configuration/registry/principal_capabilities/capability_definition.rb +43 -0
  625. data/lib/command_tower/configuration/registry/principal_capabilities/config.rb +136 -0
  626. data/lib/command_tower/configuration/signup_session/config.rb +67 -0
  627. data/lib/command_tower/configuration/signup_session/email_availability.rb +18 -0
  628. data/lib/command_tower/configuration/signup_session/rate_limits.rb +48 -0
  629. data/lib/command_tower/configuration/user/config.rb +6 -5
  630. data/lib/command_tower/credential_resolution/env_backend.rb +25 -0
  631. data/lib/command_tower/credential_resolution/smtp_action_mailer_bridge.rb +24 -0
  632. data/lib/command_tower/credential_resolution/smtp_credentials.rb +26 -0
  633. data/lib/command_tower/credential_resolution/twilio_credentials.rb +26 -0
  634. data/lib/command_tower/credential_resolution.rb +78 -0
  635. data/lib/command_tower/current.rb +19 -0
  636. data/lib/command_tower/engine.rb +23 -5
  637. data/lib/command_tower/events.rb +184 -0
  638. data/lib/command_tower/execution.rb +111 -0
  639. data/lib/command_tower/impersonation/activity_declaration.rb +23 -0
  640. data/lib/command_tower/impersonation/apply_overlay.rb +88 -0
  641. data/lib/command_tower/impersonation/clear_overlay_for_audit.rb +23 -0
  642. data/lib/command_tower/impersonation/establish_identity.rb +44 -0
  643. data/lib/command_tower/install/baseline.rb +34 -0
  644. data/lib/command_tower/install/doctor.rb +227 -0
  645. data/lib/command_tower/jwt/authorization_helper.rb +136 -0
  646. data/lib/command_tower/jwt/csrf_helper.rb +138 -0
  647. data/lib/command_tower/logging/lifecycle_declaration.rb +27 -0
  648. data/lib/command_tower/logging/projection.rb +67 -0
  649. data/lib/command_tower/logging/subscriber.rb +103 -0
  650. data/lib/command_tower/password_recovery/authorization_helper.rb +25 -0
  651. data/lib/command_tower/principal_capabilities.rb +15 -0
  652. data/lib/command_tower/redis_connection.rb +21 -0
  653. data/lib/command_tower/schema/error/email_validation_required.rb +15 -0
  654. data/lib/command_tower/schema/shared/user.rb +32 -0
  655. data/lib/command_tower/schema.rb +2 -25
  656. data/lib/command_tower/signup/authorization_helper.rb +25 -0
  657. data/lib/command_tower/spec_helper.rb +58 -1
  658. data/lib/command_tower/testing.rb +62 -0
  659. data/lib/command_tower/version.rb +1 -1
  660. data/lib/command_tower.rb +32 -0
  661. data/lib/generators/command_tower/configure/USAGE +14 -2
  662. data/lib/generators/command_tower/configure/configure_generator.rb +29 -2
  663. data/lib/tasks/auto_annotate_models.rake +23 -55
  664. data/lib/tasks/install.rake +81 -0
  665. data/lib/tasks/users.rake +119 -0
  666. data/spec/factories/entity.rb +37 -0
  667. data/spec/factories/impersonation_session.rb +17 -0
  668. data/spec/factories/messaging.rb +156 -0
  669. data/spec/factories/role.rb +29 -0
  670. data/spec/factories/user.rb +69 -0
  671. data/spec/factories/user_secret.rb +12 -0
  672. metadata +686 -55
  673. data/app/controllers/command_tower/admin_controller.rb +0 -110
  674. data/app/controllers/command_tower/auth/plain_text_controller.rb +0 -133
  675. data/app/controllers/command_tower/inbox/message_blast_controller.rb +0 -89
  676. data/app/controllers/command_tower/inbox/message_controller.rb +0 -81
  677. data/app/controllers/command_tower/user_controller.rb +0 -49
  678. data/app/controllers/command_tower/username_controller.rb +0 -26
  679. data/app/helpers/command_tower/pagination_helper.rb +0 -40
  680. data/app/helpers/command_tower/schema_helper.rb +0 -29
  681. data/app/models/message.rb +0 -30
  682. data/app/models/message_blast.rb +0 -27
  683. data/app/services/command_tower/admin_service/users.rb +0 -27
  684. data/app/services/command_tower/inbox_service/blast/delete.rb +0 -23
  685. data/app/services/command_tower/inbox_service/blast/metadata.rb +0 -26
  686. data/app/services/command_tower/inbox_service/blast/new_user_blaster.rb +0 -24
  687. data/app/services/command_tower/inbox_service/blast/retrieve.rb +0 -30
  688. data/app/services/command_tower/inbox_service/blast/upsert.rb +0 -67
  689. data/app/services/command_tower/inbox_service/message/metadata.rb +0 -41
  690. data/app/services/command_tower/inbox_service/message/modify.rb +0 -44
  691. data/app/services/command_tower/inbox_service/message/retrieve.rb +0 -36
  692. data/app/services/command_tower/inbox_service/message/send.rb +0 -33
  693. data/app/services/command_tower/jwt/time_delay_token.rb +0 -17
  694. data/app/services/command_tower/login_strategy/plain_text/create.rb +0 -43
  695. data/app/services/command_tower/login_strategy/plain_text/email_verification/generate.rb +0 -29
  696. data/app/services/command_tower/login_strategy/plain_text/email_verification/send.rb +0 -23
  697. data/app/services/command_tower/login_strategy/plain_text/email_verification/verify.rb +0 -24
  698. data/app/services/command_tower/login_strategy/plain_text/login.rb +0 -50
  699. data/app/services/command_tower/pagination_service_helper.rb +0 -88
  700. data/app/services/command_tower/user_attributes/modify.rb +0 -68
  701. data/app/services/command_tower/user_attributes/roles.rb +0 -27
  702. data/db/migrate/20250223023306_create_command_tower_messages.rb +0 -12
  703. data/db/migrate/20250223023313_create_command_tower_message_blasts.rb +0 -14
  704. data/lib/command_tower/schema/admin/users.rb +0 -16
  705. data/lib/command_tower/schema/inbox/blast_request.rb +0 -15
  706. data/lib/command_tower/schema/inbox/blast_response.rb +0 -16
  707. data/lib/command_tower/schema/inbox/message_blast_entity.rb +0 -16
  708. data/lib/command_tower/schema/inbox/message_blast_metadata.rb +0 -16
  709. data/lib/command_tower/schema/inbox/message_entity.rb +0 -14
  710. data/lib/command_tower/schema/inbox/metadata.rb +0 -18
  711. data/lib/command_tower/schema/inbox/modified.rb +0 -13
  712. data/lib/command_tower/schema/page.rb +0 -11
  713. data/lib/command_tower/schema/pagination.rb +0 -16
  714. data/lib/command_tower/schema/plain_text/create_user_request.rb +0 -18
  715. data/lib/command_tower/schema/plain_text/create_user_response.rb +0 -17
  716. data/lib/command_tower/schema/plain_text/email_verify_request.rb +0 -11
  717. data/lib/command_tower/schema/plain_text/email_verify_response.rb +0 -11
  718. data/lib/command_tower/schema/plain_text/email_verify_send_request.rb +0 -9
  719. data/lib/command_tower/schema/plain_text/email_verify_send_response.rb +0 -11
  720. data/lib/command_tower/schema/plain_text/login_request.rb +0 -15
  721. data/lib/command_tower/schema/plain_text/login_response.rb +0 -16
  722. data/lib/command_tower/schema/user.rb +0 -28
@@ -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,89 @@
1
+ # Principal capabilities
2
+
3
+ Authenticated **frontend-projectable** capability ids for the current principal. Projection is `effective RBAC entity grants ∩ curated registry` — never role/group names, and never an auto-dump of `Entity.entities`.
4
+
5
+ ```text
6
+ RBAC groups → entity grants → curated projectable registry
7
+ → possessed principalCapabilities → shared FE gating (4.6.2+)
8
+ ```
9
+
10
+ CommandTower owns which CT entities are frontend-projectable. Hosts own who receives those entities through RBAC group composition. Hosts register **host-owned** projectables only; they do **not** re-register or redefine CT capability ids.
11
+
12
+ ## Register (configuration)
13
+
14
+ ```ruby
15
+ CommandTower.configure do |config|
16
+ # Default 1:1 — id and required_entity share the same name
17
+ config.registry.principal_capabilities.capability :admin_workspace
18
+
19
+ # Host additive (example) — override required_entity when names differ
20
+ config.registry.principal_capabilities.capability :manage_wagers do |capability|
21
+ capability.required_entity = :pickem_admin_wagers
22
+ end
23
+ end
24
+ ```
25
+
26
+ | Field | Rules |
27
+ |-------|--------|
28
+ | id | DSL name; public contract |
29
+ | required_entity | RBAC entity name; defaults to id when omitted; must exist after RBAC composition |
30
+ | owner | `:command_tower` (seeded) or `:host` (host registration) |
31
+
32
+ Lookup: `CommandTower.config.registry.principal_capabilities.fetch(:admin_workspace)`.
33
+
34
+ ## Seeded CommandTower catalog (4.6.1 + 4.7.1 + 6.3)
35
+
36
+ | id / required_entity | Why projectable |
37
+ |----------------------|-----------------|
38
+ | `admin_workspace` | Gate Admin Workspace affordances without probing `/admin/workspace` |
39
+ | `admin_users` | Gate Admin Users Collection/detail presentation |
40
+ | `admin_users_update` | Gate Admin Users identity mutations (name, username, email, email validation) |
41
+ | `admin_rbac_assignments` | Gate Admin Users role assignment (assignable catalog + PATCH roles) |
42
+ | `admin_audit_events` | Gate Audit Admin affordances and deep links outside the workspace manifest |
43
+ | `admin_messaging_announcements` | Gate Messaging Admin affordances outside the manifest |
44
+ | `admin_impersonation` | Gate Impersonate affordances (5.6 composition). Possession does not start a session by itself. |
45
+ | `me_audit_events` | Gate Account **Activity** / self-audit Explorer presentation (4.7.1). Hosts own who receives the entity. |
46
+
47
+ Me/Auth route gates (`session`, `me`, …) and unimplemented Admin entities are **not** seeded. Adding a new CT RBAC entity later requires an explicit slice decision to also register a principal capability.
48
+
49
+ ## Boot validation
50
+
51
+ 1. Host `CommandTower.configure` may register additive capabilities (mutable).
52
+ 2. `after_initialize` — `principal_capabilities.finalize!` with audit / admin_workspace, then freeze (skipped in test).
53
+ 3. `to_prepare` — after RBAC, `validate_required_entities!(Entity.entities)`.
54
+
55
+ ## Runtime HTTP
56
+
57
+ `GET /auth/principal-capabilities` (engine-relative; hosts mounting at `/api` use `GET /api/auth/principal-capabilities`).
58
+
59
+ - Authn + authz: entity `principal_capabilities` (`PrincipalCapabilitiesController#show`). Grant to host `member` (and operators that need the call) like other Me/Auth surfaces.
60
+ - Controller → `Workflows::Auth::PrincipalCapabilities::ShowWorkflow` (`retry_strategy :none`) → `Services::Auth::PrincipalCapabilities::Project` → `PrincipalCapabilitiesSerializer`.
61
+ - Service: if any role has `allow_everything`, return **all** registered projectable ids; else include definitions whose `required_entity` is granted. Unique + sorted. No role-name checks.
62
+ - Envelope `data`: `{ principalCapabilities: string[] }` — possessed ids only.
63
+
64
+ | Persona (typical host composition) | Example `principalCapabilities` |
65
+ |------------------------------------|---------------------------------|
66
+ | guest | **401** |
67
+ | email verification required | **412** |
68
+ | `member` (host grants `me_audit_events`) | `me_audit_events` |
69
+ | `member` (host withholds `me_audit_events`) | `[]` |
70
+ | `audit_operator` | `admin_audit_events`, `admin_workspace` |
71
+ | `messaging_operator` | `admin_messaging_announcements`, `admin_workspace` |
72
+ | host `admin` / `operations_admin` | CT Admin seeds (not necessarily `me_audit_events`) |
73
+ | `owner` | all **registered** ids (CT + host additive) |
74
+
75
+ ## Naming collisions (different concepts)
76
+
77
+ | Name | Meaning |
78
+ |------|---------|
79
+ | `principalCapabilities` / this registry | Runtime possessed, frontend-projectable RBAC-backed facts |
80
+ | `/me` `capabilities` | Account self-service flags — **not** this projection |
81
+ | HostConfig / build-time `capabilities` | Compile-time product modules — **not** this projection |
82
+ | RBAC group / role names | Host privilege composition — **never** the FE gating contract |
83
+
84
+ ## Related
85
+
86
+ - [API reference](api_reference.md#get-authprincipal-capabilities)
87
+ - [Authorization](authorization.md)
88
+ - [Admin Workspace](admin_workspace.md) — tool manifest only; not a UI permission probe
89
+ - [Host integration](host_integration_guide.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 | Host `rbac_groups.yml` must grant CT-owned Me/Auth entity **names** to product roles; do not copy CT controller mappings. Conflicts fail at boot. |
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. Add host `rbac_groups.yml` with a product role (typically `member`) that **grants** CT-owned entity names; do not redefine engine `owner` or copy CT controller mappings. Operational Admin roles are host-owned.
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,92 @@
1
+ # Upgrade: CommandTower 0.11.0
2
+
3
+ **From:** `0.10.0`
4
+ **To:** `0.11.0`
5
+
6
+ This document summarizes **host-visible changes from `0.10.0` through the `0.11.0` release**. It is the durable upgrade / change SST for hosts already on the Auth/Me/Messaging platform. 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
+ | New migrations | Install and migrate audit events (+ scope columns) and impersonation sessions via `command_tower:install:migrations` then `db:migrate` |
13
+ | New RBAC entities | Hosts must **explicitly grant** new CT-owned entity names; they are **not** auto-added to a generic CT admin role |
14
+ | `GET /auth/principal-capabilities` | Frontend gating should use possessed projectable ids from this endpoint — **not** role names and **not** `GET /admin/workspace` as a permission probe |
15
+ | Admin HTTP during impersonation | Admin routes return **418** while an impersonation session is active |
16
+ | Admin least privilege | Operational Admin privilege is host-owned role composition; CT ships entities only |
17
+ | Optional Admin resource scoping | Hosts that need scoped Users/Audit register `config.admin_scope` and may set `scope_required` on workspace tools |
18
+
19
+ ## Platform capabilities in 0.11.0
20
+
21
+ (In addition to everything in [0.10.0](0.10.0.md).)
22
+
23
+ - **Execution context + eventing:** workflow/service lifecycle on `ActiveSupport::Notifications`; structured logging is a subscriber (not a second bus)
24
+ - **Audit ledger:** append-only `command_tower_audit_events`; registered `audit(...)` on workflows/services; Me + Admin query HTTP + filter-options
25
+ - **Admin Workspace:** tool registry + RBAC-filtered `GET /admin/workspace` manifest; optional host `admin_scope`
26
+ - **Principal capabilities:** curated FE-projectable ids via `GET /auth/principal-capabilities` (effective grants ∩ registry)
27
+ - **Impersonation:** start via Admin Users, end via `DELETE /auth/impersonation-session`; overlay identity; idle/absolute timeouts
28
+ - **Admin Users:** list/show, identity update, assignable roles, RBAC assignment — host-granted entities only
29
+
30
+ ### New engine HTTP (engine-relative)
31
+
32
+ | Method | Path |
33
+ |--------|------|
34
+ | GET | `/auth/principal-capabilities` |
35
+ | DELETE | `/auth/impersonation-session` |
36
+ | GET | `/me/audit-events`, `/me/audit-events/:id`, `/me/audit-events/filter-options` |
37
+ | GET | `/admin/workspace` |
38
+ | GET | `/admin/audit-events`, `/admin/audit-events/:id`, `/admin/audit-events/filter-options` |
39
+ | GET | `/admin/users`, `/admin/users/:id`, `/admin/users/assignable-roles` |
40
+ | PATCH | `/admin/users/:id/name`, `/username`, `/email`, `/email-validation`, `/roles` |
41
+ | POST | `/admin/users/:id/impersonation-sessions` |
42
+
43
+ ### New RBAC entities (host grants required)
44
+
45
+ | Entity | Typical grant |
46
+ |--------|----------------|
47
+ | `principal_capabilities` | `member` (and operators that need the call) |
48
+ | `me_audit_events` | `member` when Account Activity is in product |
49
+ | `admin_workspace` | host operator roles |
50
+ | `admin_audit_events` | audit operators |
51
+ | `admin_users` | users operators |
52
+ | `admin_users_update` | identity-update operators |
53
+ | `admin_rbac_assignments` | RBAC assignment operators |
54
+ | `admin_impersonation` | impersonation operators |
55
+
56
+ See dummy composition in `rails_app/config/rbac_groups.yml`.
57
+
58
+ ### Migrations
59
+
60
+ - `20260816000001_create_command_tower_audit_events.rb`
61
+ - `20260817000001_add_scope_columns_to_command_tower_audit_events.rb`
62
+ - `20260817000003_create_command_tower_impersonation_sessions.rb`
63
+
64
+ ## Documentation
65
+
66
+ - [Eventing](../eventing.md)
67
+ - [Audit](../audit.md)
68
+ - [Admin Workspace](../admin_workspace.md)
69
+ - [Principal capabilities](../principal_capabilities.md)
70
+ - Host start-here: [Host integration](../host_integration_guide.md)
71
+ - Canonical HTTP catalog: [API reference](../api_reference.md)
72
+
73
+ ## Upgrade checklist (existing 0.10.0 hosts)
74
+
75
+ 1. Bump gem to `0.11.0` and run `bin/rails command_tower:install:migrations` then `bin/rails db:migrate`.
76
+ 2. Run `bin/rails command_tower:doctor`.
77
+ 3. Grant `principal_capabilities` (and `me_audit_events` if Account Activity is in product) on host `member` / equivalent.
78
+ 4. Explicitly grant Admin entities to **host-owned** operator roles (`admin_workspace`, `admin_users`, `admin_users_update`, `admin_rbac_assignments`, `admin_audit_events`, `admin_impersonation`) — do not assume a platform `admin` role.
79
+ 5. Point Admin FE gating at `GET /auth/principal-capabilities`, not `GET /admin/workspace`.
80
+ 6. Optional: register host Admin Workspace tools, `admin_scope`, host audit events, and impersonation timeouts.
81
+ 7. Smoke: login → `GET /auth/principal-capabilities` **200**; operator with grants → `GET /admin/workspace` **200**; without grants → **403**.
82
+ 8. Smoke: start impersonation → Admin routes **418**; `DELETE /auth/impersonation-session` restores actor.
83
+
84
+ New hosts: follow [Host integration](../host_integration_guide.md) end-to-end (includes 0.10.0 foundations plus this release).
85
+
86
+ ## Related
87
+
88
+ - [Initializing](../initializing.md)
89
+ - [Extending](../extending.md)
90
+ - [Authentication & authorization guide](../authentication_authorization_guide.md)
91
+ - [README](../../README.md)
92
+ - Prior release: [0.10.0](0.10.0.md)
@@ -0,0 +1,10 @@
1
+ # Upgrades
2
+
3
+ Host-facing upgrade / change summaries for CommandTower releases.
4
+
5
+ | Version | Summary |
6
+ |---------|---------|
7
+ | [0.11.0](0.11.0.md) | Execution context, audit ledger, Admin Workspace, principal capabilities, impersonation, Admin Users |
8
+ | [0.10.0](0.10.0.md) | Modern Auth/Me/Messaging platform; SchemaHelper removal; host RBAC required |
9
+
10
+ New hosts should start with [Host integration](../host_integration_guide.md).
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module AdminScope
5
+ module ApplyAuditScoping
6
+ module_function
7
+
8
+ def call(relation:, scope_context:, principal:)
9
+ return relation if scope_context.nil?
10
+
11
+ registration = CommandTower.config.admin_scope.fetch(scope_context.tool_id)
12
+ global_event_names = global_visible_event_names
13
+ affected_user_ids = registration.affected_users_in_scope.call(
14
+ scope_value: scope_context.scope_value,
15
+ principal:,
16
+ tool_id: scope_context.tool_id
17
+ )
18
+
19
+ host_relation = registration.narrow_audit.call(
20
+ relation: relation.where(scope_class: CommandTower::Audit::Event::SCOPE_CLASSES[:host]),
21
+ scope_value: scope_context.scope_value,
22
+ principal:,
23
+ tool_id: scope_context.tool_id
24
+ )
25
+
26
+ global_relation = relation.where(
27
+ scope_class: CommandTower::Audit::Event::SCOPE_CLASSES[:global],
28
+ action: global_event_names,
29
+ affected_user_id: affected_user_ids
30
+ )
31
+
32
+ relation.where(id: host_relation.select(:id))
33
+ .or(relation.where(id: global_relation.select(:id)))
34
+ .where.not(scope_class: CommandTower::Audit::Event::SCOPE_CLASSES[:legacy])
35
+ end
36
+
37
+ def global_visible_event_names
38
+ CommandTower.config.registry.audit.definitions.filter_map do |name, definition|
39
+ name if definition.global_visible_in_host_scope?
40
+ end
41
+ end
42
+ private_class_method :global_visible_event_names
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module AdminScope
5
+ module ApplyUsersNarrowing
6
+ module_function
7
+
8
+ def call(relation:, scope_context:, principal:)
9
+ return relation if scope_context.nil?
10
+
11
+ registration = CommandTower.config.admin_scope.fetch(scope_context.tool_id)
12
+ registration.narrow_users.call(
13
+ relation:,
14
+ scope_value: scope_context.scope_value,
15
+ principal:,
16
+ tool_id: scope_context.tool_id
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module AdminScope
5
+ module ManifestProjection
6
+ IMPERSONATION_DISABLED_REASON = "Admin tools are unavailable while impersonating a user."
7
+
8
+ module_function
9
+
10
+ def call(definition:, principal:)
11
+ scope_payload = scope_payload_for(definition)
12
+ unscoped_unregistered = scope_payload.nil? && !admin_scope_registered?(definition.id)
13
+
14
+ availability = availability_for(definition, principal:)
15
+ scope_options = scope_options_for(definition, principal:)
16
+
17
+ payload = {
18
+ scope: scope_payload,
19
+ availability:,
20
+ scope_options:
21
+ }.compact
22
+
23
+ if CommandTower::Current.impersonation_active
24
+ payload[:availability] = {
25
+ enabled: false,
26
+ reason: IMPERSONATION_DISABLED_REASON
27
+ }
28
+ return payload
29
+ end
30
+
31
+ return {} if unscoped_unregistered
32
+
33
+ payload
34
+ end
35
+
36
+ def scope_payload_for(definition)
37
+ return unless definition.scope_required?
38
+
39
+ {
40
+ required: true,
41
+ parameter: camelize_parameter(definition.scope_parameter),
42
+ label: definition.scope_label
43
+ }
44
+ end
45
+ private_class_method :scope_payload_for
46
+
47
+ def availability_for(definition, principal:)
48
+ return { enabled: true, reason: nil } unless admin_scope_registered?(definition.id)
49
+
50
+ registration = CommandTower.config.admin_scope.fetch(definition.id)
51
+ normalize_availability(registration.availability.call(principal:))
52
+ end
53
+ private_class_method :availability_for
54
+
55
+ def scope_options_for(definition, principal:)
56
+ return unless definition.scope_required? && admin_scope_registered?(definition.id)
57
+
58
+ registration = CommandTower.config.admin_scope.fetch(definition.id)
59
+ Array(registration.options.call(principal:)).map do |option|
60
+ normalize_scope_option(option)
61
+ end
62
+ end
63
+ private_class_method :scope_options_for
64
+
65
+ def admin_scope_registered?(tool_id)
66
+ CommandTower.config.admin_scope.registered?(tool_id)
67
+ end
68
+ private_class_method :admin_scope_registered?
69
+
70
+ def normalize_availability(value)
71
+ hash = value.is_a?(Hash) ? value : {}
72
+ enabled = hash.fetch(:enabled, hash["enabled"])
73
+ reason = hash.fetch(:reason, hash["reason"])
74
+ {
75
+ enabled: enabled != false,
76
+ reason: reason.nil? ? nil : reason.to_s
77
+ }
78
+ end
79
+ private_class_method :normalize_availability
80
+
81
+ def normalize_scope_option(option)
82
+ case option
83
+ when AdminScope::ScopeOption
84
+ { value: option.value, label: option.label }
85
+ when Hash
86
+ {
87
+ value: option.fetch(:value, option["value"]).to_s,
88
+ label: option.fetch(:label, option["label"]).to_s
89
+ }
90
+ else
91
+ raise ArgumentError, "scope option must be ScopeOption or Hash, got #{option.class}"
92
+ end
93
+ end
94
+ private_class_method :normalize_scope_option
95
+
96
+ def camelize_parameter(parameter)
97
+ parameter.to_s.camelize(:lower)
98
+ end
99
+ private_class_method :camelize_parameter
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module AdminScope
5
+ module Resolve
6
+ module_function
7
+
8
+ def call(tool_id:, principal:, scope_value:)
9
+ definition = CommandTower.config.registry.admin_workspace.fetch(tool_id)
10
+ return nil unless definition.scope_required?
11
+
12
+ token = scope_value.to_s.strip
13
+ if token.empty?
14
+ raise CommandTower::Errors::ForbiddenError
15
+ end
16
+
17
+ registration = CommandTower.config.admin_scope.fetch(tool_id)
18
+ valid = registration.validate.call(value: token, principal:)
19
+ unless valid
20
+ raise CommandTower::Errors::ForbiddenError
21
+ end
22
+
23
+ ScopeContext.new(
24
+ tool_id: definition.id,
25
+ scope_value: token,
26
+ scope_parameter: definition.scope_parameter
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CommandTower
4
+ module AdminScope
5
+ ScopeContext = Data.define(:tool_id, :scope_value, :scope_parameter)
6
+ end
7
+ end