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
data/docs/audit.md ADDED
@@ -0,0 +1,195 @@
1
+ # Audit (authoring contract)
2
+
3
+ Explicit semantic audit is a first-class CommandTower capability on workflows and services. It is **not** inferred from lifecycle completion, ActiveRecord dirty tracking, routes, or model callbacks.
4
+
5
+ A workflow or service may emit **zero, one, or many** audit facts. **Workflows are the preferred publication boundary.** Ownership follows the **semantic fact**, not whichever layer executes SQL. Do not emit the same fact from both a workflow and a service.
6
+
7
+ Registered `command_tower.audit.*` facts persist into **one** CommandTower ledger (`command_tower_audit_events` / `CommandTower::Audit::Event`) through a **synchronous, raising** subscriber.
8
+
9
+ **Audit persistence is transaction-aware, not transaction-requiring.** `audit(...)` does not create or require a business transaction. If an ActiveRecord transaction is already active, the INSERT joins it and shares commit/rollback. If none is active, the fact persists as a standalone synchronous write. Persistence failure raises: inside a transaction that can roll back the surrounding operation; outside one, only the audit write fails loudly.
10
+
11
+ Use a transaction when the **business operation itself** requires atomicity. Emit `audit(...)` while that transaction is active when the audit fact must share the operation’s commit/rollback boundary. Otherwise `audit(...)` may persist without an enclosing transaction. An auditable workflow is not automatically a transactional workflow.
12
+
13
+ Mutation-coupled facts (`role_assigned`, `password_changed`, `phone_verified`) commonly belong inside the mutation’s transaction. Standalone facts (`session_created`, `login_failed`, `session_cleared`, `announcement_produced`, `impersonation_started`, `impersonation_ended`) need not.
14
+
15
+ Logging remains a separate consumer. Hosts do not install their own audit subscriber; the engine attaches `CommandTower::Audit::Persistence::Subscriber` on boot. Sparse hosts get the table by running `command_tower:install:migrations` then `db:migrate`.
16
+
17
+ Rows are **append-only**. Application code must not `update` or `destroy` ledger rows. Workflows and services must not `Audit::Event.create!`; only the persistence subscriber writes.
18
+
19
+ Production publishers for the catalog below **are shipped**. Query APIs read `CommandTower::Audit::Event` through List + Project services; the raw row is not the HTTP contract. Admin Workspace **backend** manifest is shipped (`GET /admin/workspace`). Shared Audit Explorer UI is shipped. Filter-options projection is shipped.
20
+
21
+ Lifecycle vs audit vs logs: [Eventing](eventing.md).
22
+
23
+ ## Register (configuration)
24
+
25
+ Registration is configuration (`class_composer`). There is no plugin `register(...)` API. CommandTower and hosts share **one** registry and **one** `command_tower.audit.*` namespace.
26
+
27
+ CommandTower-owned names are seeded by the engine. Hosts **add** names. Hosts **cannot** redefine CommandTower-owned definitions (boot/runtime fail-fast).
28
+
29
+ ```ruby
30
+ CommandTower.configure do |config|
31
+ config.registry.audit.event :wager_placed do |event|
32
+ event.enabled = true
33
+ event.user_history = true
34
+ event.label = "Wager placed"
35
+ event.tags = %w[pickem wager]
36
+ event.sensitive_fields = []
37
+ event.allowed_changes = %i[status]
38
+ event.retention = :permanent # :permanent | :ninety_days | :one_year
39
+ event.subject_required = true
40
+ event.affected_user_required = true
41
+ end
42
+ end
43
+ ```
44
+
45
+ **Policy (registration) owns:** `enabled`, `enablement_configurable`, `user_history`, `label`, `tags`, `sensitive_fields`, `allowed_changes`, `retention`, subject/affected-user required flags, **`global_visible_in_host_scope`** (default false).
46
+
47
+ **Discovery tags** (`tags`) are presentation metadata used by consumers such as Audit Explorer to help operators find event types in filter-option selectors. Tags are normalized (lowercase, stripped, unique, deterministic). They do **not** affect authorization, event publication, ledger persistence, or server-side audit filtering — there is no `?tag=` ledger filter and tags are not stored on audit rows.
48
+
49
+ **Runtime `audit(...)` owns:** occurrence name, subject, affected user, `changes`, `metadata`, explicit `attribution_mode` when required, optional **`host_context:`** (`{ type:, identifier: }`), optional **`scope_class:`** (`global` \| `host` \| `legacy`).
50
+
51
+ Lookup: `CommandTower.config.registry.audit.fetch(:wager_placed)`.
52
+
53
+ Filter-option projection (scoped catalogs for Explorer):
54
+
55
+ ```text
56
+ GET /me/audit-events/filter-options
57
+ GET /admin/audit-events/filter-options
58
+ ```
59
+
60
+ Projects `{ eventNames: [{ value, label, tags }], subjectTypes: [{ value, label }], attributionModes: […] }` from the live registry. Me includes only `user_history` events and omits attribution modes. `subjectTypes` are unique sorted registry `subject_type` values (blank omitted). Options are not authorization.
61
+
62
+ CommandTower registration defines both the canonical contract **and** whether publication enablement may be configured. Core facts are mandatory (`enablement_configurable: false`). Hosts cannot disable them. Selected noisy facts (`session_created`, `session_cleared`, `login_failed`) are enablement-configurable. Hosts may change **only** `enabled` via `CommandTower.config.registry.audit.set_enabled!(:login_failed, true)` before `finalize!`. Hosts still cannot `event :session_created`.
63
+
64
+ ```ruby
65
+ CommandTower.config.registry.audit.set_enabled!(:session_created, false)
66
+ CommandTower.config.registry.audit.set_enabled!(:login_failed, true)
67
+ ```
68
+
69
+ ## Catalog (CommandTower-owned)
70
+
71
+ | Event | Default enabled | Enablement configurable |
72
+ |-------|----------------:|------------------------:|
73
+ | `user_registered` | yes | no |
74
+ | `role_assigned` | yes | no |
75
+ | `password_changed` | yes | no |
76
+ | `email_verified` | yes | no |
77
+ | `phone_updated` | yes | no |
78
+ | `phone_cleared` | yes | no |
79
+ | `phone_verified` | yes | no |
80
+ | `announcement_produced` | yes | no |
81
+ | `impersonation_started` | yes | no |
82
+ | `impersonation_ended` | yes | no |
83
+ | `session_created` | yes | yes |
84
+ | `session_cleared` | no | yes |
85
+ | `login_failed` | no | yes |
86
+
87
+ Sensitive-field / user-history / retention stay on the **registry** for emit-time policy. At INSERT the persistence subscriber copies the **smallest historical snapshot**: `user_history`, `sensitive_fields`, and `retention`. Later registry changes must not unmask historical PII. Read APIs treat a change key as sensitive if it is in the **union** of the row snapshot and the current definition (current may be more restrictive; it cannot unmask). Account-history eligibility is **only** the snapshot `user_history` column. The snapshot does not include `enabled`, `allowed_changes`, or owner. There is no `visibility` column. Retention is stored and **not enforced** as a delete/filter yet.
88
+
89
+ Invalid names, duplicates, host overrides, and illegal policy (for example sensitive fields not in `allowed_changes`) raise `CommandTower::Audit::*` errors.
90
+
91
+ ## `audit(...)`
92
+
93
+ Available via `CommandTower::Execution::ContextAccess` on `ApplicationWorkflow` and `ServiceBase`:
94
+
95
+ ```ruby
96
+ audit(
97
+ :user_email_changed,
98
+ subject: user,
99
+ affected_user: user,
100
+ changes: { email: { from: old_email, to: new_email } },
101
+ metadata: { reason: "user_request" },
102
+ attribution_mode: nil,
103
+ subject_label: nil
104
+ )
105
+ ```
106
+
107
+ Callers do **not** build ASN strings and do **not** `Audit::Event.create!`.
108
+
109
+ Instrument name: `command_tower.audit.<registered_name>`.
110
+
111
+ ### Changes
112
+
113
+ Shape: `{ attribute: { from:, to: } }`. Empty `changes: {}` is valid (for example `password_changed`). Keys must be ⊆ registered `allowed_changes`. Extra keys **raise**; they are not dropped.
114
+
115
+ Nested hashes are published through `CommandTower::Events.publish_audit`. Generic `Events.publish` / `sanitize_payload` still keep only scalars (and arrays of scalars). The ledger column is `change_set` because ActiveRecord reserves `changes` for dirty tracking.
116
+
117
+ ### Disabled events
118
+
119
+ A registered event with `enabled = false` causes `audit(...)` to **return without emitting**. Disabled events therefore insert **no row**. Unregistered names still fail **before** persistence.
120
+
121
+ ### Attribution
122
+
123
+ | Mode | When | Actor |
124
+ |------|------|--------|
125
+ | `impersonation` | `Current.impersonation_active` | `originating_administrator_id` |
126
+ | `admin_direct` | explicit keyword | `Current.user_id` |
127
+ | `system` | explicit, or no `Current.user_id` | `nil` |
128
+ | `self_service` | default when actor id equals affected user id | `Current.user_id` |
129
+
130
+ Do not infer `admin_direct` from routes or from id inequality. `Current.user_id` remains the **effective** user under impersonation.
131
+
132
+ Lifecycle events `impersonation_started` / `impersonation_ended` record the administrator as actor and the impersonated user as affected. Product mutations executed during an overlay use `attribution_mode: impersonation` automatically from Current.
133
+
134
+ ### Fail-fast
135
+
136
+ Unregistered names, forbidden change keys, unsafe objects in `changes`/`metadata`, missing required subject/affected user, and invalid attribution **raise**. Malformed audit data is never silently discarded.
137
+
138
+ ## Transaction-aware persistence
139
+
140
+ ```text
141
+ Standalone audit
142
+ audit(...)
143
+ → INSERT
144
+
145
+ Transactional business operation
146
+ BEGIN
147
+ mutation
148
+ audit(...)
149
+ COMMIT
150
+ ```
151
+
152
+ The outer `BEGIN` exists only when the **business operation** needs atomicity. Audit does not impose it.
153
+
154
+ ## Audit publisher ownership
155
+
156
+ Workflows are the preferred owners of semantic audit facts because workflows represent canonical business orchestration. New audit publishers should be placed in the owning workflow when that can be done cleanly.
157
+
158
+ Typical shape:
159
+
160
+ ```text
161
+ orchestrate service mutation
162
+
163
+ receive meaningful result
164
+
165
+ emit semantic audit fact
166
+ ```
167
+
168
+ Services may emit audit facts when the service uniquely owns the mutation or moving publication upward would require artificial plumbing, result-contract distortion, or transaction gymnastics.
169
+
170
+ Existing service-owned audit publishers (Phase 4.3) remain supported. Consolidation toward workflow ownership is deferred platform cleanup, not a Phase 4 correctness requirement. Do not reshape service result contracts purely to satisfy audit placement without an explicit architecture decision.
171
+
172
+ Do not double-emit from workflow and service. Models, controllers, jobs, and callbacks remain inappropriate audit publishers.
173
+
174
+ ## Reading audit history
175
+
176
+ The ledger is queryable over HTTP. Controllers stay transport-only. Workflows orchestrate; they do **not** query `Audit::Event`. `Services::Audit::Events::List` owns the relation. `Services::Audit::Events::Project` duplicates hashes and masks sensitive `change_set` from/to **before** serialization. Unmasked sensitive values never leave Project toward HTTP. Both user and admin projections mask. There is no unmask permission.
177
+
178
+ | Surface | Path | RBAC entity | Scope |
179
+ |---------|------|-------------|-------|
180
+ | User | `GET /me/audit-events` | `me_audit_events` | `affected_user_id == current_user.id` **and** snapshot `user_history == true`. Actor identity never grants visibility. The Me controller does not read a target user from params. |
181
+ | Admin | `GET /admin/audit-events` | `admin_audit_events` | Full ledger when unscoped. When tool is scoped: composite host-scoped + eligible global for in-scope users; **legacy excluded**. |
182
+ | User detail | `GET /me/audit-events/:id` | `me_audit_events` | Same Me scope as list. **404** when out of scope or missing. |
183
+ | Admin detail | `GET /admin/audit-events/:id` | `admin_audit_events` | Full ledger by id when unscoped. **404** when missing or out of scope (disclosure-safe). |
184
+
185
+ **Scope provenance columns:** `scope_class` (`global` \| `host` \| `legacy`), `host_context_type`, `host_context_identifier`. Pre-scope historical rows migrated to **`legacy`** — not bulk-labeled `global`. Legacy rows are **not** eligible for scoped-admin global inclusion.
186
+
187
+ Pagination matches Inbox: `limit` default **50** (unchanged), max **100**, `offset` default 0, `meta: { limit, offset, totalCount }`. Shared Audit Explorer FE always sends `limit` from Collection pageSize (default **25**). Order is `occurred_at DESC, id DESC`.
188
+
189
+ User filters: optional `eventName` (singular alias) or `eventNames[]` (exact `action` `IN`), `occurredAfter` / `occurredBefore` (ISO8601), `subjectType` (singular alias) or `subjectTypes[]` (`IN`). Admin adds `affectedUserId`, `actorUserId`, `originatingAdministratorId`, `attributionMode`. Invalid enums/IDs are 422. Free-text / metadata JSON search is **not** supported (deferred).
190
+
191
+ Masking applies to sensitive **change keys** only (union of row `sensitive_fields` and the current registry for that action if still registered). Metadata is **not** masked: there is no metadata sensitivity grammar today. Phone uses last-four style (`*******1212`); email uses `m***@example.com`; nil stays nil; unknown/malformed values are fully redacted, never raw.
192
+
193
+ Serializer fields (camelCase), from the projection hash only: `id`, `eventName`, `eventLabel` (optional registry label; may be empty), `occurredAt`, `attributionMode`, `actor.userId`, `affectedUser.userId`, `subject.{type,id,label}`, `impersonationActive`, `originatingAdministratorId`, `changes`, `metadata`. List and show share this serializer. Snapshot internals, `event_uuid`, and execution/correlation IDs are not exposed.
194
+
195
+ Shared FE product: `@commandtower/frontend/capabilities/audit` (`AuditExplorer` scopes `admin` \| `me`). Account Activity presentation is gated by principal capability `me_audit_events`; Admin Explorer by `admin_audit_events`. Backend RBAC remains authoritative. The Admin Workspace **manifest** (`GET /admin/workspace`) discovers tools; it is not a UI permission probe.
@@ -0,0 +1,51 @@
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
+ ## Impersonation (session overlay)
28
+
29
+ Impersonation is a **CommandTower session primitive**, not a User mutation. The administrator JWT stays the credential (`user_id` is always the actor). An optional `impersonation_session_id` claim locates a server-authoritative `command_tower_impersonation_sessions` row. Product identity (`current_user`, `Current.user_id`) becomes the target while the overlay is valid.
30
+
31
+ - Start: `POST /admin/users/:id/impersonation-sessions` (RBAC `admin_impersonation`)
32
+ - Stop / return-to-self: `DELETE /auth/impersonation-session` (valid administrator JWT; not `admin_impersonation` on the target)
33
+ - Timeouts: `config.impersonation.idle_timeout` (default 10 minutes) and `config.impersonation.absolute_timeout` (default 1 hour). Idle refresh is **workflow-declared** (`impersonation_activity!`); HTTP activity alone does not refresh idle.
34
+ - Concurrent independent sessions are allowed. Nested impersonation is forbidden.
35
+ - Expired overlay on a product request: `401` `impersonation_session_expired` without clearing the auth cookie.
36
+ - Admin resource endpoints other than `GET /admin/workspace` return **418** `admin_unavailable_during_impersonation` while overlaying. Workspace remains allowed and projects every tool `availability.enabled: false`.
37
+ - Target visibility for start is **only** Phase 5.4 Admin Resource Scoping (`ScopeResolution` + Admin Users Show). Unscoped hosts omit `adminScope`.
38
+
39
+ See [API reference](api_reference.md#impersonation) and [Authorization](authorization.md).
40
+
41
+ ## Where to go next
42
+
43
+ | Need | Guide |
44
+ |------|-------|
45
+ | Full authn + RBAC design | [Authentication & authorization guide](authentication_authorization_guide.md) |
46
+ | Cookie / CORS / CSRF | [Cookie authentication](cookie_authentication_guide.md) |
47
+ | HTTP contracts | [API reference](api_reference.md) |
48
+ | Session invalidation | [Sensitive changes](sensitive_routes.md) |
49
+ | Authorization (RBAC) | [Authorization](authorization.md) |
50
+
51
+ Back to [README](../README.md).
@@ -0,0 +1,154 @@
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
+ | Principal capabilities | `GET /auth/principal-capabilities` |
65
+ | Name | `PATCH /me/name` |
66
+ | Password change | `PATCH /me/password` |
67
+ | Identity policy | `GET /auth/identity-policy` |
68
+ | Signup session | `POST /auth/signup-session` → availability GETs |
69
+ | Email verification | `POST /auth/email-verification/{send,verify}` |
70
+ | Password recovery | `POST /auth/password-recovery-session` → `POST /auth/password-reset/{send,validate,reset}` |
71
+
72
+ There is **no** `GET /user`, `POST /user/modify`, `POST /auth/login` (without `plain-text`), or `POST /auth/password/change` on the modern engine.
73
+
74
+ ## Session boundaries
75
+
76
+ ### Signup session
77
+
78
+ 1. `POST /auth/signup-session` → `signupSessionToken`
79
+ 2. Call gated availability endpoints with `Authorization: Signup …`
80
+
81
+ Used by email/username availability. Missing/invalid/expired session → `401` with `signup_session_*` codes.
82
+
83
+ ### Password recovery session
84
+
85
+ 1. `POST /auth/password-recovery-session` → `recoverySessionToken`
86
+ 2. `POST /auth/password-reset/send` requires `Authorization: Recovery …`
87
+
88
+ Validate/reset use the emailed reset token in the body (public). See [password_reset_workflow.md](password_reset_workflow.md).
89
+
90
+ ## RBAC
91
+
92
+ ### Engine defaults
93
+
94
+ `lib/command_tower/authorization/default.yml`:
95
+
96
+ - **`owner`** — `entities: true` (full access). Explicit top-level authority; distinct from host operational Admin roles.
97
+
98
+ There is **no** CommandTower operational `admin` role. Admin capabilities are CT-owned **entities** (`admin_workspace`, `admin_users`, `admin_impersonation`, `admin_audit_events`, `admin_messaging_announcements`, …) that hosts grant to their own roles. Impersonation start is `admin_impersonation`; it is not included in dummy `admin` / `operations_admin`. Nested impersonation is forbidden at HTTP **418** while overlaying (workflow nested 403 if reached). Stop is a session primitive (`DELETE /auth/impersonation-session`), not an Admin Users mutation. Other Admin resource endpoints return **418** `admin_unavailable_during_impersonation` during overlay except `GET /admin/workspace` (tools disabled).
99
+
100
+ ### Host RBAC file (required for Me/Auth)
101
+
102
+ `AuthorizeRequest` fails closed when the caller’s roles do not grant the CT-owned entity for the action. CommandTower ships those entity definitions. Hosts supply `config/rbac_groups.yml` with **product roles** that grant entity **names** (default path already `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
+ c.authorization.default_membership_role = "member"
108
+ end
109
+ ```
110
+
111
+ Dummy host example: `rails_app/config/rbac_groups.yml` — **`member`** grants session, me, profile, inbox, preferences, phone, pushover, email verification; operator roles grant selected Admin entities; a host-owned **`admin`** may deliberately grant a broad Admin bundle. Do not redefine `owner` or copy CT controller/entity blocks. Hosts may define an `admin` role as host policy.
112
+
113
+ Admin announcements: assign users a host role that includes `admin_messaging_announcements` (for example a host `admin` or `messaging_operator`).
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 **host-owned** RBAC entities in the host YAML. Grant CT-owned entity names to product roles; do not copy engine controller mappings.
128
+
129
+ ## Engine admin HTTP
130
+
131
+ ```http
132
+ GET /admin/workspace
133
+ GET /admin/users
134
+ GET /admin/users/:id
135
+ POST /admin/users/:id/impersonation-sessions
136
+ GET /admin/audit-events
137
+ POST /admin/messaging/announcements
138
+ DELETE /auth/impersonation-session
139
+ ```
140
+
141
+ Impersonation is a session overlay, not a User mutation and not an Admin Workspace tool. There is no role-assign or attribute-modify admin surface.
142
+
143
+ ## Email verification gate
144
+
145
+ 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).
146
+
147
+ ## Related
148
+
149
+ - [API reference](api_reference.md)
150
+ - [Cookie authentication](cookie_authentication_guide.md)
151
+ - [Sensitive changes](sensitive_routes.md)
152
+ - [Initializing](initializing.md)
153
+ - [Messaging](messaging_integration_guide.md)
154
+ - [README](../README.md)
@@ -0,0 +1,55 @@
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). CommandTower ships CT-owned entity definitions. Hosts grant those names to product roles; they must not copy CT controller mappings. Without a host role that grants Me/Auth entities, authenticated calls return **403**.
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
+ ## Capability ownership vs role composition
18
+
19
+ **Platform owns capabilities.** CommandTower defines RBAC entities (Me/Auth surfaces and Admin capabilities such as `admin_workspace`, `admin_users`, `admin_impersonation`, `admin_audit_events`, `admin_messaging_announcements`).
20
+
21
+ **Hosts own privilege bundles.** Operational/admin roles are host-defined groups that grant selected CT entities (and optional host entities). Installing CommandTower does **not** automatically grant operational Admin access.
22
+
23
+ ## Roles and host mapping
24
+
25
+ Engine defaults in `lib/command_tower/authorization/default.yml`:
26
+
27
+ - `owner` — explicit full access (`entities: true`). Distinct from host-defined operational Admin roles.
28
+ - CT-owned **entities** for Me, session, profile, inbox, preferences, phone, Pushover, email verification, admin announcements, admin audit events, Admin Users, impersonation start, and the Admin Workspace manifest
29
+ - **No** CommandTower operational `admin` role. New Admin entities require explicit host grants and must not accumulate into a platform Admin bundle.
30
+
31
+ Hosts grant `admin_impersonation` explicitly. Dummy `admin` / `operations_admin` do **not** include it; `impersonation_operator` does. `owner` keeps `entities: true`.
32
+
33
+ Stop / return-to-self (`DELETE /auth/impersonation-session`) is a session primitive: it authenticates the administrator JWT and does not authorize `admin_impersonation` on the effective (target) user.
34
+
35
+ While overlaying, Admin resource endpoints other than `GET /admin/workspace` return **418** `admin_unavailable_during_impersonation`. Workspace remains allowed so the operator can see disabled tiles. Impersonation start consumes Admin Resource Scoping as the sole target-visibility contract.
36
+
37
+ Hosts define product roles (for example `member`, `audit_operator`, or a deliberate host-owned `admin`) that **grant entity names**. See dummy host `rails_app/config/rbac_groups.yml`. Do not redefine `owner` or CT **entity** identifiers. A host may define a broad `admin` role as **host policy**; that is allowed. Composition is additive; conflicts fail at boot.
38
+
39
+ Optional: `config.authorization.default_membership_role = "member"` assigns that composed role atomically on register.
40
+
41
+ ## Principal capabilities (FE projection)
42
+
43
+ `GET /auth/principal-capabilities` returns possessed **frontend-projectable** ids from effective entity grants ∩ `config.registry.principal_capabilities`. Groups compose privilege; they are never the FE gating contract. See [Principal capabilities](principal_capabilities.md).
44
+
45
+ ## Where to go next
46
+
47
+ | Need | Guide |
48
+ |------|-------|
49
+ | Full RBAC entities, roles, failure shapes | [Authentication & authorization guide](authentication_authorization_guide.md) |
50
+ | Frontend-projectable capability ids | [Principal capabilities](principal_capabilities.md) |
51
+ | Admin Workspace least privilege | [Admin Workspace](admin_workspace.md) |
52
+ | Authentication | [Authentication](authentication.md) |
53
+ | Install / config | [Initializing](initializing.md) |
54
+
55
+ Back to [README](../README.md).
@@ -0,0 +1,67 @@
1
+ # Authenticated Password Change Workflow
2
+
3
+ Authenticated password change lets a signed-in user replace their password by proving knowledge of the current password. On success, CommandTower rotates `verifier_token` in the same transaction as the password digest update, invalidating **all** existing JWT sessions (including the caller’s). The API does **not** re-issue a JWT; hosts must clear local auth and send the user through Sign In again.
4
+
5
+ This is distinct from [password recovery](password_reset_workflow.md), which is public and recovery-session/token-based.
6
+
7
+ ## Purpose and orchestration
8
+
9
+ | Item | Value |
10
+ |------|--------|
11
+ | Route | `PATCH /me/password` (engine-relative) |
12
+ | Auth | Authentication + authorization boundaries |
13
+ | Controller | `CommandTower::Me::PasswordController#update` |
14
+ | Workflow | `CommandTower::Workflows::Me::ChangePasswordWorkflow` (`ApplicationWorkflow`, `retry_strategy :none`) |
15
+ | Service | `CommandTower::Services::Me::ChangePassword` |
16
+ | Deserializer | `CommandTower::Deserializers::Me::ChangePasswordDeserializer` |
17
+ | Serializer | `CommandTower::Serializers::Me::ChangePasswordResponseSerializer` |
18
+ | Feature gate | Route always drawn (not constrained in `routes.rb`) |
19
+
20
+ **Boundary:** The controller deserializes input and runs **one** workflow. The workflow orchestrates the change-password service and maps failures to HTTP status. It does not implement password rules itself.
21
+
22
+ ## Request
23
+
24
+ ```json
25
+ {
26
+ "current_password": "string",
27
+ "password": "string",
28
+ "password_confirmation": "string"
29
+ }
30
+ ```
31
+
32
+ ## Success (200)
33
+
34
+ Serialized success payload (message only). Response effects include `clear_token: true` so cookie/header sessions are cleared. No JWT, verifier, or password material is returned.
35
+
36
+ ## Service flow (under the workflow)
37
+
38
+ 1. Authenticate `current_password`
39
+ 2. Confirm `password` matches `password_confirmation`
40
+ 3. Enforce configured password length bounds
41
+ 4. In one DB transaction: persist new password digest → `reset_verifier_token!`
42
+ 5. On failure: typed application errors → workflow HTTP mapping
43
+
44
+ ## Failures
45
+
46
+ | Condition | Typical status |
47
+ |-----------|----------------|
48
+ | Missing / invalid JWT | 401 |
49
+ | Validation / bad current password / confirmation | 4xx via Me error mapping |
50
+ | Unexpected infra failure | 5xx |
51
+
52
+ ## Contrast with recovery reset
53
+
54
+ | | Authenticated change | Forgot / reset |
55
+ |--|----------------------|----------------|
56
+ | Proof | Current password | Recovery session + reset token |
57
+ | Route | `PATCH /me/password` | `POST /auth/password-recovery-session` + `/auth/password-reset/*` |
58
+ | Auth | JWT | Public (+ recovery session) |
59
+ | Verifier | Rotated | See recovery guide |
60
+ | Post-success | All JWTs invalid; host → Sign In | Host-defined |
61
+
62
+ ## Related
63
+
64
+ - [Sensitive changes](sensitive_routes.md)
65
+ - [API reference](api_reference.md)
66
+ - [Password reset workflow](password_reset_workflow.md)
67
+ - [Extending](extending.md)
@@ -0,0 +1,41 @@
1
+ # Controllers and routes
2
+
3
+ CommandTower exposes platform HTTP through `CommandTower::Engine` routes. Hosts typically mount the engine (for example at `/api`) via the configure generator or a custom mount.
4
+
5
+ This page is an **index** of route areas. Detailed request/response contracts live in the [API reference](api_reference.md).
6
+
7
+ ## Route areas
8
+
9
+ | Area | Prefix (engine-relative) | Purpose |
10
+ |------|--------------------------|---------|
11
+ | Auth session | `/auth/*` | Login, logout, session, register, signup-session, identity policy, principal-capabilities, availability |
12
+ | Email verification | `/auth/email-verification/*` | Send / verify email codes |
13
+ | Password recovery | `/auth/password-recovery-session`, `/auth/password-reset/*` | Forgot / reset password |
14
+ | Me / profile | `/me`, `/profile`, `/me/name`, `/me/password` | Account reads and updates |
15
+ | Inbox | `/me/inbox*` | User inbox consume (list, open, archive, bulk ops) |
16
+ | Audit events | `/me/audit-events`, `/admin/audit-events` | User and admin audit history reads (masked) |
17
+ | Admin Users | `/admin/users` | Read-only Admin user list/show |
18
+ | Impersonation | `/admin/users/:id/impersonation-sessions`, `/auth/impersonation-session` | Start overlay; stop / return-to-self |
19
+ | Admin Workspace | `/admin/workspace` | RBAC-filtered admin tool manifest |
20
+ | Preferences | `/me/preferences*` | Notification preferences |
21
+ | Phone | `/me/phone*` | Phone endpoint + verification |
22
+ | Pushover | `/me/pushover*` | Pushover endpoint lifecycle + verification |
23
+ | Admin messaging | `/admin/messaging/announcements` | Cohort announcements |
24
+
25
+ Exact paths depend on where the host mounts the engine.
26
+
27
+ ## Controllers
28
+
29
+ Engine controllers are transport adapters: authenticate/authorize as required, deserialize, run **exactly one** workflow, render the application envelope. See [Architecture](architecture.md).
30
+
31
+ ## Related
32
+
33
+ - [API reference](api_reference.md) — endpoint catalog
34
+ - [Principal capabilities](principal_capabilities.md) — FE-projectable possession projection
35
+ - [Admin Workspace](admin_workspace.md) — registry and manifest
36
+ - [Messaging](messaging_integration_guide.md) — messaging surfaces summary
37
+ - [Extending](extending.md) — host extension boundaries
38
+ - [Initializing](initializing.md) — mount and configure
39
+ - [README](../README.md)
40
+
41
+ HTTP request specs for the engine live under `spec/requests/`.