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.
- checksums.yaml +4 -4
- data/README.md +106 -29
- data/app/auth/command_tower/auth/auth_context.rb +42 -0
- data/app/auth/command_tower/auth/password_recovery_session_context.rb +11 -0
- data/app/auth/command_tower/auth/signup_session_context.rb +11 -0
- data/app/controllers/command_tower/admin/application_controller.rb +27 -0
- data/app/controllers/command_tower/admin/audit/events_controller.rb +61 -0
- data/app/controllers/command_tower/admin/messaging/announcements_controller.rb +27 -0
- data/app/controllers/command_tower/admin/users/identities_controller.rb +77 -0
- data/app/controllers/command_tower/admin/users/impersonation_sessions_controller.rb +33 -0
- data/app/controllers/command_tower/admin/users/roles_controller.rb +40 -0
- data/app/controllers/command_tower/admin/users_controller.rb +46 -0
- data/app/controllers/command_tower/admin/workspace_controller.rb +15 -0
- data/app/controllers/command_tower/application_controller.rb +97 -15
- data/app/controllers/command_tower/auth/email/availability_controller.rb +35 -0
- data/app/controllers/command_tower/auth/email_verification/send_controller.rb +24 -0
- data/app/controllers/command_tower/auth/email_verification/verify_controller.rb +41 -0
- data/app/controllers/command_tower/auth/identity_policy_controller.rb +14 -0
- data/app/controllers/command_tower/auth/impersonation_session_controller.rb +27 -0
- data/app/controllers/command_tower/auth/logout_controller.rb +16 -0
- data/app/controllers/command_tower/auth/password_recovery_session/create_controller.rb +17 -0
- data/app/controllers/command_tower/auth/password_reset/reset_controller.rb +35 -0
- data/app/controllers/command_tower/auth/password_reset/send_controller.rb +40 -0
- data/app/controllers/command_tower/auth/password_reset/validate_controller.rb +35 -0
- data/app/controllers/command_tower/auth/plain_text/login_controller.rb +22 -0
- data/app/controllers/command_tower/auth/principal_capabilities_controller.rb +19 -0
- data/app/controllers/command_tower/auth/register_controller.rb +33 -0
- data/app/controllers/command_tower/auth/session_controller.rb +21 -0
- data/app/controllers/command_tower/auth/signup_session/create_controller.rb +17 -0
- data/app/controllers/command_tower/auth/username/availability_controller.rb +35 -0
- data/app/controllers/command_tower/me/audit_events_controller.rb +59 -0
- data/app/controllers/command_tower/me/inbox_controller.rb +85 -0
- data/app/controllers/command_tower/me/name_controller.rb +37 -0
- data/app/controllers/command_tower/me/password_controller.rb +37 -0
- data/app/controllers/command_tower/me/phone_controller.rb +44 -0
- data/app/controllers/command_tower/me/phone_verification/verifications_controller.rb +23 -0
- data/app/controllers/command_tower/me/phone_verification/verify_controller.rb +34 -0
- data/app/controllers/command_tower/me/preferences_controller.rb +46 -0
- data/app/controllers/command_tower/me/pushover/verifications_controller.rb +23 -0
- data/app/controllers/command_tower/me/pushover_controller.rb +66 -0
- data/app/controllers/command_tower/me_controller.rb +19 -0
- data/app/controllers/command_tower/profile_controller.rb +19 -0
- data/app/controllers/concerns/command_tower/api/application_response_renderer.rb +87 -0
- data/app/controllers/concerns/command_tower/api/deserializer_failure_details.rb +22 -0
- data/app/controllers/concerns/command_tower/auth/authentication_boundary.rb +35 -0
- data/app/controllers/concerns/command_tower/auth/authorization_boundary.rb +26 -0
- data/app/controllers/concerns/command_tower/auth/invalid_credentials_deserializer_renderer.rb +23 -0
- data/app/controllers/concerns/command_tower/auth/password_recovery_session_boundary.rb +29 -0
- data/app/controllers/concerns/command_tower/auth/signup_session_boundary.rb +29 -0
- data/app/controllers/concerns/command_tower/execution/http_boundary.rb +65 -0
- data/app/deserializers/command_tower/deserializers/admin/messaging/create_announcement_deserializer.rb +106 -0
- data/app/deserializers/command_tower/deserializers/admin/scope_parameter.rb +49 -0
- data/app/deserializers/command_tower/deserializers/admin/users.rb +218 -0
- data/app/deserializers/command_tower/deserializers/application_deserializer.rb +148 -0
- data/app/deserializers/command_tower/deserializers/audit/events.rb +224 -0
- data/app/deserializers/command_tower/deserializers/auth/email_availability_deserializer.rb +19 -0
- data/app/deserializers/command_tower/deserializers/auth/email_verification/verify_deserializer.rb +21 -0
- data/app/deserializers/command_tower/deserializers/auth/password_reset/reset_deserializer.rb +31 -0
- data/app/deserializers/command_tower/deserializers/auth/password_reset/send_deserializer.rb +21 -0
- data/app/deserializers/command_tower/deserializers/auth/password_reset/validate_deserializer.rb +23 -0
- data/app/deserializers/command_tower/deserializers/auth/plain_text/login_deserializer.rb +24 -0
- data/app/deserializers/command_tower/deserializers/auth/register_deserializer.rb +42 -0
- data/app/deserializers/command_tower/deserializers/auth/username_availability_deserializer.rb +19 -0
- data/app/deserializers/command_tower/deserializers/clients/errors.rb +45 -0
- data/app/deserializers/command_tower/deserializers/clients/missing.rb +10 -0
- data/app/deserializers/command_tower/deserializers/clients/payload.rb +21 -0
- data/app/deserializers/command_tower/deserializers/clients/result.rb +122 -0
- data/app/deserializers/command_tower/deserializers/clients/types.rb +219 -0
- data/app/deserializers/command_tower/deserializers/coercion_result.rb +35 -0
- data/app/deserializers/command_tower/deserializers/failure.rb +14 -0
- data/app/deserializers/command_tower/deserializers/me/change_password_deserializer.rb +41 -0
- data/app/deserializers/command_tower/deserializers/me/phone_verification/verify_deserializer.rb +29 -0
- data/app/deserializers/command_tower/deserializers/me/pushover/credentials_deserializer.rb +31 -0
- data/app/deserializers/command_tower/deserializers/me/update_name_deserializer.rb +22 -0
- data/app/deserializers/command_tower/deserializers/me/update_phone_deserializer.rb +22 -0
- data/app/deserializers/command_tower/deserializers/messaging/inbox.rb +77 -0
- data/app/deserializers/command_tower/deserializers/messaging/preferences/show_deserializer.rb +17 -0
- data/app/deserializers/command_tower/deserializers/messaging/preferences/update_deserializer.rb +97 -0
- data/app/errors/command_tower/errors/account/phone_already_verified_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_missing_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_code_invalid_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_expired_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_send_failed_error.rb +25 -0
- data/app/errors/command_tower/errors/account/phone_verification_stale_error.rb +17 -0
- data/app/errors/command_tower/errors/account/phone_verification_throttled_error.rb +24 -0
- data/app/errors/command_tower/errors/account/pushover_already_configured_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_capability_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_not_configured_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_provider_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/account/pushover_verification_failed_error.rb +27 -0
- data/app/errors/command_tower/errors/account/sms_capability_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/application_error.rb +27 -0
- data/app/errors/command_tower/errors/auth/admin_unavailable_during_impersonation_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/csrf_mismatch_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/csrf_missing_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/default_membership_assignment_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/email_already_registered_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/email_verification_required_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/impersonation_session_expired_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/impersonation_session_missing_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/invalid_credentials_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/nested_impersonation_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/password_recovery_ip_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_expired_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_invalid_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_missing_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_recovery_session_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/password_reset_invalid_token_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/password_reset_unavailable_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/self_impersonation_error.rb +21 -0
- data/app/errors/command_tower/errors/auth/signup_ip_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/signup_session_expired_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/signup_session_invalid_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/signup_session_missing_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/signup_session_rate_limit_error.rb +25 -0
- data/app/errors/command_tower/errors/auth/verification_code_invalid_error.rb +17 -0
- data/app/errors/command_tower/errors/auth/verification_send_failed_error.rb +25 -0
- data/app/errors/command_tower/errors/continuation_exhausted_error.rb +25 -0
- data/app/errors/command_tower/errors/forbidden_error.rb +19 -0
- data/app/errors/command_tower/errors/internal_error.rb +19 -0
- data/app/errors/command_tower/errors/messaging/accept_rejected_error.rb +21 -0
- data/app/errors/command_tower/errors/messaging/idempotency_conflict_error.rb +21 -0
- data/app/errors/command_tower/errors/messaging/recipient_unresolved_error.rb +21 -0
- data/app/errors/command_tower/errors/not_found_error.rb +19 -0
- data/app/errors/command_tower/errors/unauthorized_error.rb +19 -0
- data/app/errors/command_tower/errors/validation_error.rb +15 -0
- data/app/jobs/command_tower/application_job.rb +7 -0
- data/app/jobs/command_tower/execution/job_boundary.rb +19 -0
- data/app/jobs/command_tower/messaging/channel_delivery_execution_job.rb +18 -0
- data/app/jobs/command_tower/messaging/communications/produce_recipient_job.rb +24 -0
- data/app/jobs/command_tower/messaging/execution_recovery_job.rb +13 -0
- data/app/jobs/command_tower/messaging/handoff_job.rb +15 -0
- data/app/jobs/command_tower/messaging/handoff_recovery_job.rb +13 -0
- data/app/mailers/command_tower/application_mailer.rb +11 -1
- data/app/mailers/command_tower/email_verification_mailer.rb +5 -3
- data/app/mailers/command_tower/messaging/channel_mailer.rb +24 -0
- data/app/mailers/command_tower/password_reset_mailer.rb +18 -0
- data/app/models/command_tower/audit/event.rb +36 -0
- data/app/models/command_tower/impersonation/session.rb +51 -0
- data/app/models/command_tower/messaging/channel_delivery.rb +45 -0
- data/app/models/command_tower/messaging/communication.rb +60 -0
- data/app/models/command_tower/messaging/delivery_attempt.rb +28 -0
- data/app/models/command_tower/messaging/destination_plan.rb +17 -0
- data/app/models/command_tower/messaging/endpoint.rb +98 -0
- data/app/models/command_tower/messaging/endpoint_pushover_credential.rb +18 -0
- data/app/models/command_tower/messaging/inbox_item.rb +52 -0
- data/app/models/command_tower/messaging/notification_preference.rb +21 -0
- data/app/models/user.rb +4 -1
- data/app/serializers/command_tower/serializers/admin/messaging/announcement_response_serializer.rb +41 -0
- data/app/serializers/command_tower/serializers/admin/users.rb +69 -0
- data/app/serializers/command_tower/serializers/admin/workspace/manifest_serializer.rb +58 -0
- data/app/serializers/command_tower/serializers/application/envelope_serializer.rb +25 -0
- data/app/serializers/command_tower/serializers/application/error_entry_serializer.rb +18 -0
- data/app/serializers/command_tower/serializers/application_serializer.rb +26 -0
- data/app/serializers/command_tower/serializers/audit/events/filter_options_serializer.rb +44 -0
- data/app/serializers/command_tower/serializers/audit/events.rb +42 -0
- data/app/serializers/command_tower/serializers/auth/email_availability_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/auth/email_verification/message_response_serializer.rb +15 -0
- data/app/serializers/command_tower/serializers/auth/identity_policy_serializer.rb +36 -0
- data/app/serializers/command_tower/serializers/auth/login_response_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/auth/logout_response_serializer.rb +13 -0
- data/app/serializers/command_tower/serializers/auth/password_recovery_session_response_serializer.rb +16 -0
- data/app/serializers/command_tower/serializers/auth/password_reset/message_response_serializer.rb +15 -0
- data/app/serializers/command_tower/serializers/auth/password_reset/validate_response_serializer.rb +22 -0
- data/app/serializers/command_tower/serializers/auth/principal_capabilities_serializer.rb +15 -0
- data/app/serializers/command_tower/serializers/auth/register_response_serializer.rb +16 -0
- data/app/serializers/command_tower/serializers/auth/session_response_serializer.rb +40 -0
- data/app/serializers/command_tower/serializers/auth/signup_session_response_serializer.rb +16 -0
- data/app/serializers/command_tower/serializers/auth/user_serializer.rb +21 -0
- data/app/serializers/command_tower/serializers/auth/username_availability_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/impersonation/session_serializer.rb +19 -0
- data/app/serializers/command_tower/serializers/me/account_serializer.rb +45 -0
- data/app/serializers/command_tower/serializers/me/change_password_response_serializer.rb +13 -0
- data/app/serializers/command_tower/serializers/me/pushover_serializer.rb +59 -0
- data/app/serializers/command_tower/serializers/messaging/inbox.rb +45 -0
- data/app/serializers/command_tower/serializers/messaging/preferences/catalog_serializer.rb +17 -0
- data/app/serializers/command_tower/serializers/messaging/preferences/category_serializer.rb +23 -0
- data/app/serializers/command_tower/serializers/messaging/preferences/notification_serializer.rb +30 -0
- data/app/serializers/command_tower/serializers/profile/profile_serializer.rb +13 -0
- data/app/services/command_tower/README.md +24 -14
- data/app/services/command_tower/auth/request_context.rb +21 -0
- data/app/services/command_tower/authorize/validate.rb +58 -19
- data/app/services/command_tower/clients/client_base.rb +183 -0
- data/app/services/command_tower/clients/client_result.rb +45 -0
- data/app/services/command_tower/clients/const_resolution.rb +32 -0
- data/app/services/command_tower/clients/decoded_response.rb +16 -0
- data/app/services/command_tower/clients/endpoint_base.rb +242 -0
- data/app/services/command_tower/clients/endpoint_input.rb +18 -0
- data/app/services/command_tower/clients/errors/authentication_error.rb +26 -0
- data/app/services/command_tower/clients/errors/configuration_error.rb +10 -0
- data/app/services/command_tower/clients/errors/deserialization_error.rb +26 -0
- data/app/services/command_tower/clients/errors/discovery_error.rb +10 -0
- data/app/services/command_tower/clients/errors/upstream_error.rb +26 -0
- data/app/services/command_tower/clients/invocation.rb +61 -0
- data/app/services/command_tower/clients/namespace_proxy.rb +75 -0
- data/app/services/command_tower/clients/scoped_client.rb +88 -0
- data/app/services/command_tower/clients/serialized_request.rb +17 -0
- data/app/services/command_tower/clients/transport/error.rb +18 -0
- data/app/services/command_tower/clients/transport/faraday_adapter.rb +129 -0
- data/app/services/command_tower/clients/transport/faraday_config.rb +45 -0
- data/app/services/command_tower/clients/transport/request.rb +50 -0
- data/app/services/command_tower/clients/transport/response.rb +22 -0
- data/app/services/command_tower/clients/url.rb +56 -0
- data/app/services/command_tower/identity/phone/normalizer.rb +41 -0
- data/app/services/command_tower/identity/phone_verification/sms_configuration.rb +58 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/fake_adapter.rb +35 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/log_adapter.rb +34 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport/adapters/twilio_adapter.rb +60 -0
- data/app/services/command_tower/identity/phone_verification/sms_transport.rb +42 -0
- data/app/services/command_tower/jwt/authenticate_user.rb +67 -34
- data/app/services/command_tower/jwt/authentication_outcome.rb +37 -0
- data/app/services/command_tower/jwt/decode.rb +27 -8
- data/app/services/command_tower/jwt/encode.rb +4 -6
- data/app/services/command_tower/jwt/login_create.rb +10 -8
- data/app/services/command_tower/login_strategy/plain_text/email_verification/required.rb +11 -9
- data/app/services/command_tower/messaging/accept/channel_delivery_result.rb +13 -0
- data/app/services/command_tower/messaging/accept/coordinator.rb +166 -0
- data/app/services/command_tower/messaging/accept/error.rb +9 -0
- data/app/services/command_tower/messaging/accept/excluded_destination.rb +24 -0
- data/app/services/command_tower/messaging/accept/handoff_scheduler.rb +40 -0
- data/app/services/command_tower/messaging/accept/idempotency_conflict_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/illegal_override_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/impossible_mandatory_plan_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/invalid_preference_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/invariant_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/operation_logger.rb +97 -0
- data/app/services/command_tower/messaging/accept/persistence_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/persister.rb +90 -0
- data/app/services/command_tower/messaging/accept/request_normalizer.rb +100 -0
- data/app/services/command_tower/messaging/accept/result.rb +99 -0
- data/app/services/command_tower/messaging/accept/unknown_type_error.rb +9 -0
- data/app/services/command_tower/messaging/accept/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/channel_detectors.rb +52 -0
- data/app/services/command_tower/messaging/channels/definition.rb +24 -0
- data/app/services/command_tower/messaging/channels/error.rb +9 -0
- data/app/services/command_tower/messaging/channels/unknown_channel_error.rb +9 -0
- data/app/services/command_tower/messaging/channels.rb +85 -0
- data/app/services/command_tower/messaging/contract/communications.rb +29 -0
- data/app/services/command_tower/messaging/contract/error.rb +9 -0
- data/app/services/command_tower/messaging/contract/internal/finder.rb +43 -0
- data/app/services/command_tower/messaging/contract/invariant_error.rb +9 -0
- data/app/services/command_tower/messaging/contract/mappers/channel_delivery_mapper.rb +53 -0
- data/app/services/command_tower/messaging/contract/mappers/communication_mapper.rb +61 -0
- data/app/services/command_tower/messaging/contract/mappers/delivery_attempt_mapper.rb +27 -0
- data/app/services/command_tower/messaging/contract/mappers/destination_plan_mapper.rb +17 -0
- data/app/services/command_tower/messaging/contract/mappers/inbox_item_mapper.rb +17 -0
- data/app/services/command_tower/messaging/contract/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/contract/observability/correlation.rb +19 -0
- data/app/services/command_tower/messaging/contract/observability/operation_logger.rb +101 -0
- data/app/services/command_tower/messaging/contract/observability/publisher.rb +44 -0
- data/app/services/command_tower/messaging/contract/requests/find_communication.rb +21 -0
- data/app/services/command_tower/messaging/contract/results/channel_delivery_result.rb +25 -0
- data/app/services/command_tower/messaging/contract/results/communication_result.rb +22 -0
- data/app/services/command_tower/messaging/contract/results/delivery_attempt_result.rb +21 -0
- data/app/services/command_tower/messaging/contract/results/destination_plan_result.rb +11 -0
- data/app/services/command_tower/messaging/contract/results/inbox_item_result.rb +11 -0
- data/app/services/command_tower/messaging/contract/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/delivery_retry_policy.rb +30 -0
- data/app/services/command_tower/messaging/endpoints/begin_verification.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/channel_gate.rb +39 -0
- data/app/services/command_tower/messaging/endpoints/conflict_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/create.rb +181 -0
- data/app/services/command_tower/messaging/endpoints/error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/fingerprinter.rb +50 -0
- data/app/services/command_tower/messaging/endpoints/invalid_transition_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/lifecycle.rb +37 -0
- data/app/services/command_tower/messaging/endpoints/mark_invalid.rb +32 -0
- data/app/services/command_tower/messaging/endpoints/mark_verification_failed.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/mark_verified.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/persistence.rb +91 -0
- data/app/services/command_tower/messaging/endpoints/pushover/verify.rb +101 -0
- data/app/services/command_tower/messaging/endpoints/query.rb +28 -0
- data/app/services/command_tower/messaging/endpoints/replace.rb +181 -0
- data/app/services/command_tower/messaging/endpoints/reset_verification.rb +13 -0
- data/app/services/command_tower/messaging/endpoints/revoke.rb +40 -0
- data/app/services/command_tower/messaging/endpoints/safe_view.rb +50 -0
- data/app/services/command_tower/messaging/endpoints/secret_box.rb +86 -0
- data/app/services/command_tower/messaging/endpoints/secret_reader.rb +72 -0
- data/app/services/command_tower/messaging/endpoints/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/endpoints/validators/push.rb +24 -0
- data/app/services/command_tower/messaging/endpoints/validators/pushover.rb +68 -0
- data/app/services/command_tower/messaging/endpoints/validators.rb +36 -0
- data/app/services/command_tower/messaging/endpoints/verification.rb +39 -0
- data/app/services/command_tower/messaging/endpoints/verification_failed_error.rb +18 -0
- data/app/services/command_tower/messaging/endpoints/verification_transition.rb +20 -0
- data/app/services/command_tower/messaging/endpoints.rb +55 -0
- data/app/services/command_tower/messaging/execution/adapter_request.rb +45 -0
- data/app/services/command_tower/messaging/execution/adapter_result.rb +81 -0
- data/app/services/command_tower/messaging/execution/adapters/email/adapter.rb +78 -0
- data/app/services/command_tower/messaging/execution/adapters/email/configuration.rb +55 -0
- data/app/services/command_tower/messaging/execution/adapters/fake_adapter.rb +34 -0
- data/app/services/command_tower/messaging/execution/adapters/pushover/adapter.rb +107 -0
- data/app/services/command_tower/messaging/execution/adapters/pushover/configuration.rb +30 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/adapter.rb +90 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/configuration.rb +58 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/log_adapter.rb +34 -0
- data/app/services/command_tower/messaging/execution/adapters/sms/twilio_http_client.rb +70 -0
- data/app/services/command_tower/messaging/execution/adapters/unconfigured_adapter.rb +20 -0
- data/app/services/command_tower/messaging/execution/attempt_ledger.rb +146 -0
- data/app/services/command_tower/messaging/execution/claim.rb +35 -0
- data/app/services/command_tower/messaging/execution/invalid_adapter_contract_error.rb +9 -0
- data/app/services/command_tower/messaging/execution/operation_logger.rb +237 -0
- data/app/services/command_tower/messaging/execution/readiness_terminal_mapping.rb +37 -0
- data/app/services/command_tower/messaging/execution/recovery.rb +78 -0
- data/app/services/command_tower/messaging/execution/resolve_recipient_address.rb +58 -0
- data/app/services/command_tower/messaging/execution/revalidate_readiness.rb +73 -0
- data/app/services/command_tower/messaging/execution/scheduler.rb +38 -0
- data/app/services/command_tower/messaging/execution/select_adapter.rb +63 -0
- data/app/services/command_tower/messaging/handoff/advance_communication.rb +39 -0
- data/app/services/command_tower/messaging/handoff/operation_logger.rb +78 -0
- data/app/services/command_tower/messaging/handoff/recovery.rb +26 -0
- data/app/services/command_tower/messaging/inbox/bulk_result.rb +19 -0
- data/app/services/command_tower/messaging/inbox/error.rb +9 -0
- data/app/services/command_tower/messaging/inbox/invalid_bulk_items_error.rb +19 -0
- data/app/services/command_tower/messaging/inbox/invariant_error.rb +9 -0
- data/app/services/command_tower/messaging/inbox/item_result.rb +64 -0
- data/app/services/command_tower/messaging/inbox/list_result.rb +18 -0
- data/app/services/command_tower/messaging/inbox/mutator.rb +252 -0
- data/app/services/command_tower/messaging/inbox/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/inbox/operation_logger.rb +37 -0
- data/app/services/command_tower/messaging/inbox/reader.rb +117 -0
- data/app/services/command_tower/messaging/inbox/scope.rb +24 -0
- data/app/services/command_tower/messaging/inbox/unread_count_result.rb +16 -0
- data/app/services/command_tower/messaging/inbox/validation_error.rb +9 -0
- data/app/services/command_tower/messaging/inbox.rb +61 -0
- data/app/services/command_tower/messaging/notification_types/catalog_category.rb +19 -0
- data/app/services/command_tower/messaging/notification_types/declaration.rb +96 -0
- data/app/services/command_tower/messaging/notification_types/declaration_validator.rb +152 -0
- data/app/services/command_tower/messaging/notification_types/duplicate_type_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/invalid_declaration_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types/registry.rb +103 -0
- data/app/services/command_tower/messaging/notification_types/sealed_registry_error.rb +9 -0
- data/app/services/command_tower/messaging/notification_types.rb +37 -0
- data/app/services/command_tower/messaging/planner/destination_plan.rb +41 -0
- data/app/services/command_tower/messaging/planner/error.rb +9 -0
- data/app/services/command_tower/messaging/planner/excluded_destination.rb +28 -0
- data/app/services/command_tower/messaging/planner/illegal_override_error.rb +9 -0
- data/app/services/command_tower/messaging/planner/impossible_mandatory_plan_error.rb +9 -0
- data/app/services/command_tower/messaging/planner/invalid_evaluation_error.rb +9 -0
- data/app/services/command_tower/messaging/planner/message_overrides.rb +33 -0
- data/app/services/command_tower/messaging/planner/operation_logger.rb +28 -0
- data/app/services/command_tower/messaging/planner/planner.rb +261 -0
- data/app/services/command_tower/messaging/planner/unknown_type_error.rb +9 -0
- data/app/services/command_tower/messaging/planner.rb +25 -0
- data/app/services/command_tower/messaging/preferences/catalog.rb +96 -0
- data/app/services/command_tower/messaging/preferences/catalog_category_result.rb +19 -0
- data/app/services/command_tower/messaging/preferences/catalog_notification_result.rb +55 -0
- data/app/services/command_tower/messaging/preferences/catalog_result.rb +13 -0
- data/app/services/command_tower/messaging/preferences/error.rb +9 -0
- data/app/services/command_tower/messaging/preferences/evaluation_result.rb +61 -0
- data/app/services/command_tower/messaging/preferences/evaluator.rb +194 -0
- data/app/services/command_tower/messaging/preferences/invalid_preference_state_error.rb +9 -0
- data/app/services/command_tower/messaging/preferences/invalid_preference_write_error.rb +10 -0
- data/app/services/command_tower/messaging/preferences/not_settings_visible_error.rb +10 -0
- data/app/services/command_tower/messaging/preferences/preference_state.rb +61 -0
- data/app/services/command_tower/messaging/preferences/reason_classes.rb +17 -0
- data/app/services/command_tower/messaging/preferences/resolve.rb +50 -0
- data/app/services/command_tower/messaging/preferences/store.rb +126 -0
- data/app/services/command_tower/messaging/preferences/store_error.rb +10 -0
- data/app/services/command_tower/messaging/preferences/suppressed_destination.rb +24 -0
- data/app/services/command_tower/messaging/preferences/unknown_type_error.rb +9 -0
- data/app/services/command_tower/messaging/preferences/update.rb +248 -0
- data/app/services/command_tower/messaging/preferences.rb +68 -0
- data/app/services/command_tower/messaging/pushover/adapters/disabled_adapter.rb +33 -0
- data/app/services/command_tower/messaging/pushover/adapters/fake_adapter.rb +90 -0
- data/app/services/command_tower/messaging/pushover/adapters/http_adapter.rb +145 -0
- data/app/services/command_tower/messaging/pushover/adapters/log_adapter.rb +52 -0
- data/app/services/command_tower/messaging/pushover/result.rb +34 -0
- data/app/services/command_tower/messaging/pushover/transport.rb +64 -0
- data/app/services/command_tower/messaging/recipient_readiness/channel_result.rb +55 -0
- data/app/services/command_tower/messaging/recipient_readiness/error.rb +9 -0
- data/app/services/command_tower/messaging/recipient_readiness/evaluate.rb +319 -0
- data/app/services/command_tower/messaging/recipient_readiness/identity_facts.rb +54 -0
- data/app/services/command_tower/messaging/recipient_readiness/reason_codes.rb +33 -0
- data/app/services/command_tower/messaging/recipient_readiness/recipient_not_found_error.rb +9 -0
- data/app/services/command_tower/messaging/recipient_readiness/recipient_result.rb +38 -0
- data/app/services/command_tower/messaging/recipient_readiness/unknown_channel_error.rb +9 -0
- data/app/services/command_tower/messaging/recipient_readiness.rb +26 -0
- data/app/services/command_tower/messaging/rendering/channel_renderer.rb +127 -0
- data/app/services/command_tower/messaging/rendering/render_error.rb +24 -0
- data/app/services/command_tower/messaging/rendering/rendered_payload.rb +44 -0
- data/app/services/command_tower/messaging/rendering/rendered_pushover_payload.rb +43 -0
- data/app/services/command_tower/messaging/rendering/rendered_sms_payload.rb +40 -0
- data/app/services/command_tower/messaging.rb +35 -0
- data/app/services/command_tower/secrets/cleanse.rb +19 -5
- data/app/services/command_tower/secrets/generate.rb +62 -27
- data/app/services/command_tower/secrets/verify.rb +46 -12
- data/app/services/command_tower/secrets.rb +2 -0
- data/app/services/command_tower/service_base.rb +30 -43
- data/app/services/command_tower/service_logging.rb +2 -36
- data/app/services/command_tower/services/account/clear_phone.rb +33 -0
- data/app/services/command_tower/services/account/phone_verification/send.rb +114 -0
- data/app/services/command_tower/services/account/phone_verification/verify.rb +89 -0
- data/app/services/command_tower/services/account/pushover/create.rb +28 -0
- data/app/services/command_tower/services/account/pushover/ct_support.rb +70 -0
- data/app/services/command_tower/services/account/pushover/destroy.rb +30 -0
- data/app/services/command_tower/services/account/pushover/replace.rb +28 -0
- data/app/services/command_tower/services/account/pushover/show.rb +19 -0
- data/app/services/command_tower/services/account/pushover/verify.rb +30 -0
- data/app/services/command_tower/services/account/update_phone.rb +58 -0
- data/app/services/command_tower/services/admin/users.rb +377 -0
- data/app/services/command_tower/services/admin/workspace/manifest.rb +49 -0
- data/app/services/command_tower/services/application_service.rb +58 -0
- data/app/services/command_tower/services/audit/events/filter_options.rb +75 -0
- data/app/services/command_tower/services/audit/events.rb +199 -0
- data/app/services/command_tower/services/auth/assign_default_membership_role.rb +44 -0
- data/app/services/command_tower/services/auth/authenticate_session.rb +132 -0
- data/app/services/command_tower/services/auth/authorize_request.rb +34 -0
- data/app/services/command_tower/services/auth/client_ip_resolver.rb +13 -0
- data/app/services/command_tower/services/auth/email_availability.rb +42 -0
- data/app/services/command_tower/services/auth/email_verification/send.rb +65 -0
- data/app/services/command_tower/services/auth/email_verification/verify.rb +53 -0
- data/app/services/command_tower/services/auth/identity_policy.rb +60 -0
- data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_send.rb +48 -0
- data/app/services/command_tower/services/auth/password_recovery/rate_limits/check_token_issue.rb +35 -0
- data/app/services/command_tower/services/auth/password_recovery/rate_limits/enforcement.rb +53 -0
- data/app/services/command_tower/services/auth/password_recovery_session/create.rb +26 -0
- data/app/services/command_tower/services/auth/password_recovery_session/decode.rb +29 -0
- data/app/services/command_tower/services/auth/password_recovery_session/encode.rb +31 -0
- data/app/services/command_tower/services/auth/password_recovery_session/validate.rb +58 -0
- data/app/services/command_tower/services/auth/password_reset/reset.rb +75 -0
- data/app/services/command_tower/services/auth/password_reset/send.rb +77 -0
- data/app/services/command_tower/services/auth/password_reset/token_verification.rb +68 -0
- data/app/services/command_tower/services/auth/password_reset/validate.rb +29 -0
- data/app/services/command_tower/services/auth/plain_text/login.rb +52 -0
- data/app/services/command_tower/services/auth/principal_capabilities/project.rb +48 -0
- data/app/services/command_tower/services/auth/register.rb +77 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/check_availability.rb +52 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/check_register.rb +24 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/check_token_issue.rb +33 -0
- data/app/services/command_tower/services/auth/signup_rate_limits/enforcement.rb +50 -0
- data/app/services/command_tower/services/auth/signup_session/create.rb +26 -0
- data/app/services/command_tower/services/auth/signup_session/decode.rb +29 -0
- data/app/services/command_tower/services/auth/signup_session/encode.rb +31 -0
- data/app/services/command_tower/services/auth/signup_session/validate.rb +58 -0
- data/app/services/command_tower/services/auth/username_availability.rb +28 -0
- data/app/services/command_tower/services/impersonation/create.rb +29 -0
- data/app/services/command_tower/services/impersonation/end.rb +28 -0
- data/app/services/command_tower/services/impersonation/record_activity.rb +30 -0
- data/app/services/command_tower/services/impersonation/terminate_open_sessions.rb +51 -0
- data/app/services/command_tower/services/me/capabilities.rb +43 -0
- data/app/services/command_tower/services/me/change_password.rb +96 -0
- data/app/services/command_tower/services/me/pushover_product_gate.rb +18 -0
- data/app/services/command_tower/services/me/sms_product_gate.rb +19 -0
- data/app/services/command_tower/services/me/update_name.rb +58 -0
- data/app/services/command_tower/services/messaging/communications/produce.rb +68 -0
- data/app/services/command_tower/services/messaging/communications/produce_many.rb +154 -0
- data/app/services/command_tower/services/messaging/inbox.rb +129 -0
- data/app/services/command_tower/services/messaging/preferences/platform_enabled_channels.rb +20 -0
- data/app/services/command_tower/services/messaging/preferences/show.rb +36 -0
- data/app/services/command_tower/services/messaging/preferences/update.rb +43 -0
- data/app/services/command_tower/services/messaging/recipients.rb +20 -0
- data/app/services/command_tower/services/rate_limits/check.rb +31 -0
- data/app/services/command_tower/services/service_result.rb +73 -0
- data/app/services/command_tower/user_attributes/mutate.rb +154 -0
- data/app/services/command_tower/username/available.rb +24 -11
- data/app/shared_sequences/command_tower/shared_sequences/admin/users/resolve_scoped_user.rb +42 -0
- data/app/views/command_tower/email_verification_mailer/verify_email.html.erb +70 -22
- data/app/views/command_tower/messaging/rendering/email.html.erb +28 -0
- data/app/views/command_tower/messaging/rendering/email.text.erb +7 -0
- data/app/views/command_tower/messaging/rendering/pushover.text.erb +1 -0
- data/app/views/command_tower/messaging/rendering/sms.text.erb +1 -0
- data/app/views/command_tower/password_reset_mailer/reset_password.html.erb +107 -0
- data/app/views/layouts/mailer.html.erb +13 -0
- data/app/views/layouts/mailer.text.erb +1 -0
- data/app/workflows/command_tower/transactional.rb +65 -0
- data/app/workflows/command_tower/workflows/admin/messaging/create_announcement_workflow.rb +73 -0
- data/app/workflows/command_tower/workflows/admin/scope_resolution.rb +17 -0
- data/app/workflows/command_tower/workflows/admin/users/error_mapping.rb +26 -0
- data/app/workflows/command_tower/workflows/admin/users/identity_mutation.rb +30 -0
- data/app/workflows/command_tower/workflows/admin/users/list_assignable_roles_workflow.rb +30 -0
- data/app/workflows/command_tower/workflows/admin/users/list_workflow.rb +54 -0
- data/app/workflows/command_tower/workflows/admin/users/set_email_validated_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/admin/users/show_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/admin/users/update_email_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/admin/users/update_name_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/admin/users/update_roles_workflow.rb +61 -0
- data/app/workflows/command_tower/workflows/admin/users/update_username_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/admin/workspace/manifest_workflow.rb +25 -0
- data/app/workflows/command_tower/workflows/application_workflow.rb +207 -0
- data/app/workflows/command_tower/workflows/audit/error_mapping.rb +24 -0
- data/app/workflows/command_tower/workflows/audit/events/filter_options_for_admin_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/audit/events/filter_options_for_user_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/audit/events/list_for_admin_workflow.rb +74 -0
- data/app/workflows/command_tower/workflows/audit/events/list_for_user_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/audit/events/show_for_admin_workflow.rb +54 -0
- data/app/workflows/command_tower/workflows/audit/events/show_for_user_workflow.rb +42 -0
- data/app/workflows/command_tower/workflows/auth/authenticate_request_workflow.rb +41 -0
- data/app/workflows/command_tower/workflows/auth/authentication_response_effects.rb +25 -0
- data/app/workflows/command_tower/workflows/auth/authorize_request_workflow.rb +28 -0
- data/app/workflows/command_tower/workflows/auth/email_availability_workflow.rb +39 -0
- data/app/workflows/command_tower/workflows/auth/email_verification/send_workflow.rb +32 -0
- data/app/workflows/command_tower/workflows/auth/email_verification/verify_workflow.rb +35 -0
- data/app/workflows/command_tower/workflows/auth/identity_error_status.rb +36 -0
- data/app/workflows/command_tower/workflows/auth/identity_policy_workflow.rb +26 -0
- data/app/workflows/command_tower/workflows/auth/logout_workflow.rb +59 -0
- data/app/workflows/command_tower/workflows/auth/password_recovery_session/authenticate_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/auth/password_recovery_session/create_workflow.rb +39 -0
- data/app/workflows/command_tower/workflows/auth/password_reset/reset_workflow.rb +40 -0
- data/app/workflows/command_tower/workflows/auth/password_reset/send_workflow.rb +45 -0
- data/app/workflows/command_tower/workflows/auth/password_reset/validate_workflow.rb +40 -0
- data/app/workflows/command_tower/workflows/auth/plain_text/login_workflow.rb +48 -0
- data/app/workflows/command_tower/workflows/auth/principal_capabilities/show_workflow.rb +27 -0
- data/app/workflows/command_tower/workflows/auth/register_workflow.rb +115 -0
- data/app/workflows/command_tower/workflows/auth/session/show_workflow.rb +58 -0
- data/app/workflows/command_tower/workflows/auth/session_error_status.rb +31 -0
- data/app/workflows/command_tower/workflows/auth/signup_error_status.rb +31 -0
- data/app/workflows/command_tower/workflows/auth/signup_session/authenticate_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/auth/signup_session/create_workflow.rb +37 -0
- data/app/workflows/command_tower/workflows/auth/username_availability_workflow.rb +39 -0
- data/app/workflows/command_tower/workflows/impersonation/error_mapping.rb +27 -0
- data/app/workflows/command_tower/workflows/impersonation/start_workflow.rb +97 -0
- data/app/workflows/command_tower/workflows/impersonation/stop_workflow.rb +66 -0
- data/app/workflows/command_tower/workflows/me/change_password_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/clear_phone_workflow.rb +52 -0
- data/app/workflows/command_tower/workflows/me/error_mapping.rb +46 -0
- data/app/workflows/command_tower/workflows/me/error_status.rb +28 -0
- data/app/workflows/command_tower/workflows/me/phone_verification/send_workflow.rb +65 -0
- data/app/workflows/command_tower/workflows/me/phone_verification/verify_workflow.rb +57 -0
- data/app/workflows/command_tower/workflows/me/pushover/create_workflow.rb +38 -0
- data/app/workflows/command_tower/workflows/me/pushover/destroy_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/pushover/replace_workflow.rb +38 -0
- data/app/workflows/command_tower/workflows/me/pushover/show_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/pushover/verify_workflow.rb +34 -0
- data/app/workflows/command_tower/workflows/me/pushover/workflow_support.rb +31 -0
- data/app/workflows/command_tower/workflows/me/show_workflow.rb +33 -0
- data/app/workflows/command_tower/workflows/me/update_name_workflow.rb +49 -0
- data/app/workflows/command_tower/workflows/me/update_phone_workflow.rb +55 -0
- data/app/workflows/command_tower/workflows/messaging/communications/produce_recipient_workflow.rb +60 -0
- data/app/workflows/command_tower/workflows/messaging/error_mapping.rb +26 -0
- data/app/workflows/command_tower/workflows/messaging/execution/deliver_workflow.rb +234 -0
- data/app/workflows/command_tower/workflows/messaging/handoff/process_workflow.rb +90 -0
- data/app/workflows/command_tower/workflows/messaging/inbox.rb +97 -0
- data/app/workflows/command_tower/workflows/messaging/preferences/show_workflow.rb +31 -0
- data/app/workflows/command_tower/workflows/messaging/preferences/update_workflow.rb +37 -0
- data/app/workflows/command_tower/workflows/profile/show_workflow.rb +28 -0
- data/app/workflows/command_tower/workflows/workflow_result.rb +58 -0
- data/config/routes.rb +105 -37
- data/db/migrate/{20241117043720_create_command_tower_users.rb → 20260805000001_create_command_tower_users.rb} +7 -9
- data/db/migrate/{20241204065708_create_command_tower_user_secrets.rb → 20260805000002_create_command_tower_user_secrets.rb} +3 -0
- data/db/migrate/20260805000003_create_command_tower_messaging_core.rb +90 -0
- data/db/migrate/20260805000004_create_messaging_notification_preferences.rb +17 -0
- data/db/migrate/20260805000005_create_messaging_endpoints_and_pushover_credentials.rb +72 -0
- data/db/migrate/20260816000001_create_command_tower_audit_events.rb +45 -0
- data/db/migrate/20260817000001_add_scope_columns_to_command_tower_audit_events.rb +34 -0
- data/db/migrate/20260817000003_create_command_tower_impersonation_sessions.rb +26 -0
- data/docs/admin_workspace.md +158 -0
- data/docs/api_reference.md +599 -0
- data/docs/architecture.md +35 -0
- data/docs/audit.md +195 -0
- data/docs/authentication.md +51 -0
- data/docs/authentication_authorization_guide.md +154 -0
- data/docs/authorization.md +55 -0
- data/docs/change_password_workflow.md +67 -0
- data/docs/controllers.md +41 -0
- data/docs/cookie_authentication_guide.md +167 -0
- data/docs/email_verification_workflow.md +58 -0
- data/docs/eventing.md +179 -0
- data/docs/extending.md +136 -0
- data/docs/host_integration_guide.md +249 -0
- data/docs/initializing.md +169 -0
- data/docs/messaging_integration_guide.md +87 -0
- data/docs/models.md +55 -0
- data/docs/pagination.md +47 -0
- data/docs/password_reset_workflow.md +82 -0
- data/docs/principal_capabilities.md +89 -0
- data/docs/sensitive_routes.md +47 -0
- data/docs/testing.md +74 -0
- data/docs/upgrades/0.10.0.md +58 -0
- data/docs/upgrades/0.11.0.md +92 -0
- data/docs/upgrades/README.md +10 -0
- data/lib/command_tower/admin_scope/apply_audit_scoping.rb +45 -0
- data/lib/command_tower/admin_scope/apply_users_narrowing.rb +21 -0
- data/lib/command_tower/admin_scope/manifest_projection.rb +102 -0
- data/lib/command_tower/admin_scope/resolve.rb +31 -0
- data/lib/command_tower/admin_scope/scope_context.rb +7 -0
- data/lib/command_tower/admin_scope/scope_option.rb +7 -0
- data/lib/command_tower/admin_scope.rb +20 -0
- data/lib/command_tower/admin_workspace.rb +16 -0
- data/lib/command_tower/audit/attribution.rb +90 -0
- data/lib/command_tower/audit/emit.rb +113 -0
- data/lib/command_tower/audit/masking.rb +41 -0
- data/lib/command_tower/audit/payload.rb +128 -0
- data/lib/command_tower/audit/persistence/subscriber.rb +85 -0
- data/lib/command_tower/audit.rb +27 -0
- data/lib/command_tower/authorization/assignable_roles.rb +36 -0
- data/lib/command_tower/authorization/default.yml +101 -35
- data/lib/command_tower/authorization/effective_entity_grants.rb +55 -0
- data/lib/command_tower/authorization/entity.rb +15 -5
- data/lib/command_tower/authorization/role.rb +5 -4
- data/lib/command_tower/authorization.rb +71 -10
- data/lib/command_tower/configuration/admin_scope/config.rb +104 -0
- data/lib/command_tower/configuration/admin_scope/tool_registration.rb +28 -0
- data/lib/command_tower/configuration/application/config.rb +2 -2
- data/lib/command_tower/configuration/authorization/config.rb +6 -0
- data/lib/command_tower/configuration/base.rb +0 -1
- data/lib/command_tower/configuration/config.rb +51 -1
- data/lib/command_tower/configuration/credentials/config.rb +33 -0
- data/lib/command_tower/configuration/credentials/smtp.rb +33 -0
- data/lib/command_tower/configuration/credentials/twilio.rb +33 -0
- data/lib/command_tower/configuration/email/config.rb +1 -18
- data/lib/command_tower/configuration/identity/config.rb +19 -0
- data/lib/command_tower/configuration/identity/phone_verification.rb +54 -0
- data/lib/command_tower/configuration/impersonation/config.rb +38 -0
- data/lib/command_tower/configuration/jwt/config.rb +7 -0
- data/lib/command_tower/configuration/jwt/cookie/config.rb +64 -0
- data/lib/command_tower/configuration/jwt/cookie/csrf/config.rb +72 -0
- data/lib/command_tower/configuration/login/strategy/plain_text/config.rb +6 -0
- data/lib/command_tower/configuration/login/strategy/plain_text/email_verify.rb +5 -0
- data/lib/command_tower/configuration/login/strategy/plain_text/password_reset.rb +49 -0
- data/lib/command_tower/configuration/messaging/config.rb +60 -0
- data/lib/command_tower/configuration/messaging/pushover.rb +42 -0
- data/lib/command_tower/configuration/messaging/sms.rb +32 -0
- data/lib/command_tower/configuration/password_recovery_session/config.rb +61 -0
- data/lib/command_tower/configuration/password_recovery_session/rate_limits.rb +33 -0
- data/lib/command_tower/configuration/registry/admin_workspace/config.rb +199 -0
- data/lib/command_tower/configuration/registry/admin_workspace/tool_definition.rb +150 -0
- data/lib/command_tower/configuration/registry/audit/config.rb +403 -0
- data/lib/command_tower/configuration/registry/audit/event_definition.rb +155 -0
- data/lib/command_tower/configuration/registry/config.rb +34 -0
- data/lib/command_tower/configuration/registry/principal_capabilities/capability_definition.rb +43 -0
- data/lib/command_tower/configuration/registry/principal_capabilities/config.rb +136 -0
- data/lib/command_tower/configuration/signup_session/config.rb +67 -0
- data/lib/command_tower/configuration/signup_session/email_availability.rb +18 -0
- data/lib/command_tower/configuration/signup_session/rate_limits.rb +48 -0
- data/lib/command_tower/configuration/user/config.rb +6 -5
- data/lib/command_tower/credential_resolution/env_backend.rb +25 -0
- data/lib/command_tower/credential_resolution/smtp_action_mailer_bridge.rb +24 -0
- data/lib/command_tower/credential_resolution/smtp_credentials.rb +26 -0
- data/lib/command_tower/credential_resolution/twilio_credentials.rb +26 -0
- data/lib/command_tower/credential_resolution.rb +78 -0
- data/lib/command_tower/current.rb +19 -0
- data/lib/command_tower/engine.rb +23 -5
- data/lib/command_tower/events.rb +184 -0
- data/lib/command_tower/execution.rb +111 -0
- data/lib/command_tower/impersonation/activity_declaration.rb +23 -0
- data/lib/command_tower/impersonation/apply_overlay.rb +88 -0
- data/lib/command_tower/impersonation/clear_overlay_for_audit.rb +23 -0
- data/lib/command_tower/impersonation/establish_identity.rb +44 -0
- data/lib/command_tower/install/baseline.rb +34 -0
- data/lib/command_tower/install/doctor.rb +227 -0
- data/lib/command_tower/jwt/authorization_helper.rb +136 -0
- data/lib/command_tower/jwt/csrf_helper.rb +138 -0
- data/lib/command_tower/logging/lifecycle_declaration.rb +27 -0
- data/lib/command_tower/logging/projection.rb +67 -0
- data/lib/command_tower/logging/subscriber.rb +103 -0
- data/lib/command_tower/password_recovery/authorization_helper.rb +25 -0
- data/lib/command_tower/principal_capabilities.rb +15 -0
- data/lib/command_tower/redis_connection.rb +21 -0
- data/lib/command_tower/schema/error/email_validation_required.rb +15 -0
- data/lib/command_tower/schema/shared/user.rb +32 -0
- data/lib/command_tower/schema.rb +2 -25
- data/lib/command_tower/signup/authorization_helper.rb +25 -0
- data/lib/command_tower/spec_helper.rb +58 -1
- data/lib/command_tower/testing.rb +62 -0
- data/lib/command_tower/version.rb +1 -1
- data/lib/command_tower.rb +32 -0
- data/lib/generators/command_tower/configure/USAGE +14 -2
- data/lib/generators/command_tower/configure/configure_generator.rb +29 -2
- data/lib/tasks/auto_annotate_models.rake +23 -55
- data/lib/tasks/install.rake +81 -0
- data/lib/tasks/users.rake +119 -0
- data/spec/factories/entity.rb +37 -0
- data/spec/factories/impersonation_session.rb +17 -0
- data/spec/factories/messaging.rb +156 -0
- data/spec/factories/role.rb +29 -0
- data/spec/factories/user.rb +69 -0
- data/spec/factories/user_secret.rb +12 -0
- metadata +686 -55
- data/app/controllers/command_tower/admin_controller.rb +0 -110
- data/app/controllers/command_tower/auth/plain_text_controller.rb +0 -133
- data/app/controllers/command_tower/inbox/message_blast_controller.rb +0 -89
- data/app/controllers/command_tower/inbox/message_controller.rb +0 -81
- data/app/controllers/command_tower/user_controller.rb +0 -49
- data/app/controllers/command_tower/username_controller.rb +0 -26
- data/app/helpers/command_tower/pagination_helper.rb +0 -40
- data/app/helpers/command_tower/schema_helper.rb +0 -29
- data/app/models/message.rb +0 -30
- data/app/models/message_blast.rb +0 -27
- data/app/services/command_tower/admin_service/users.rb +0 -27
- data/app/services/command_tower/inbox_service/blast/delete.rb +0 -23
- data/app/services/command_tower/inbox_service/blast/metadata.rb +0 -26
- data/app/services/command_tower/inbox_service/blast/new_user_blaster.rb +0 -24
- data/app/services/command_tower/inbox_service/blast/retrieve.rb +0 -30
- data/app/services/command_tower/inbox_service/blast/upsert.rb +0 -67
- data/app/services/command_tower/inbox_service/message/metadata.rb +0 -41
- data/app/services/command_tower/inbox_service/message/modify.rb +0 -44
- data/app/services/command_tower/inbox_service/message/retrieve.rb +0 -36
- data/app/services/command_tower/inbox_service/message/send.rb +0 -33
- data/app/services/command_tower/jwt/time_delay_token.rb +0 -17
- data/app/services/command_tower/login_strategy/plain_text/create.rb +0 -43
- data/app/services/command_tower/login_strategy/plain_text/email_verification/generate.rb +0 -29
- data/app/services/command_tower/login_strategy/plain_text/email_verification/send.rb +0 -23
- data/app/services/command_tower/login_strategy/plain_text/email_verification/verify.rb +0 -24
- data/app/services/command_tower/login_strategy/plain_text/login.rb +0 -50
- data/app/services/command_tower/pagination_service_helper.rb +0 -88
- data/app/services/command_tower/user_attributes/modify.rb +0 -68
- data/app/services/command_tower/user_attributes/roles.rb +0 -27
- data/db/migrate/20250223023306_create_command_tower_messages.rb +0 -12
- data/db/migrate/20250223023313_create_command_tower_message_blasts.rb +0 -14
- data/lib/command_tower/schema/admin/users.rb +0 -16
- data/lib/command_tower/schema/inbox/blast_request.rb +0 -15
- data/lib/command_tower/schema/inbox/blast_response.rb +0 -16
- data/lib/command_tower/schema/inbox/message_blast_entity.rb +0 -16
- data/lib/command_tower/schema/inbox/message_blast_metadata.rb +0 -16
- data/lib/command_tower/schema/inbox/message_entity.rb +0 -14
- data/lib/command_tower/schema/inbox/metadata.rb +0 -18
- data/lib/command_tower/schema/inbox/modified.rb +0 -13
- data/lib/command_tower/schema/page.rb +0 -11
- data/lib/command_tower/schema/pagination.rb +0 -16
- data/lib/command_tower/schema/plain_text/create_user_request.rb +0 -18
- data/lib/command_tower/schema/plain_text/create_user_response.rb +0 -17
- data/lib/command_tower/schema/plain_text/email_verify_request.rb +0 -11
- data/lib/command_tower/schema/plain_text/email_verify_response.rb +0 -11
- data/lib/command_tower/schema/plain_text/email_verify_send_request.rb +0 -9
- data/lib/command_tower/schema/plain_text/email_verify_send_response.rb +0 -11
- data/lib/command_tower/schema/plain_text/login_request.rb +0 -15
- data/lib/command_tower/schema/plain_text/login_response.rb +0 -16
- data/lib/command_tower/schema/user.rb +0 -28
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Host Integration Guide
|
|
2
|
+
|
|
3
|
+
**Start here** for integrating CommandTower into a new Rails host.
|
|
4
|
+
|
|
5
|
+
This page owns the **order of operations**. It does not replace specialty docs: install details stay in [Initializing](initializing.md), HTTP contracts in [API reference](api_reference.md), boundaries in [Extending](extending.md).
|
|
6
|
+
|
|
7
|
+
`command_tower:doctor` green means secrets/migrations look sane. It does **not** prove Me/Auth authorization works.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
- A Rails host application (engine is mounted into the host)
|
|
12
|
+
- `gem "command_tower"` in the host Gemfile (path, git, or released gem)
|
|
13
|
+
- Host-owned database (and any host Redis/job stack your app already uses)
|
|
14
|
+
|
|
15
|
+
## Step 1 — Install
|
|
16
|
+
|
|
17
|
+
Follow [Initializing](initializing.md):
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bundle install
|
|
21
|
+
bin/rails command_tower:install
|
|
22
|
+
bin/rails db:migrate
|
|
23
|
+
bin/rails command_tower:doctor
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
That copies CT migrations, generates `config/initializers/command_tower.rb` (unless skipped), and mounts `CommandTower::Engine` (unless skipped).
|
|
27
|
+
|
|
28
|
+
## Step 2 — Secrets and initializer
|
|
29
|
+
|
|
30
|
+
In the host initializer, set at least:
|
|
31
|
+
|
|
32
|
+
- `config.jwt.hmac_secret`
|
|
33
|
+
- `config.signup_session.jwt_secret` (or `SIGNUP_SESSION_JWT_SECRET`)
|
|
34
|
+
- `config.password_recovery_session.jwt_secret` (or `PASSWORD_RECOVERY_SESSION_JWT_SECRET`)
|
|
35
|
+
|
|
36
|
+
Re-run `bin/rails command_tower:doctor`. Details: [Initializing — Configuration](initializing.md#configuration).
|
|
37
|
+
|
|
38
|
+
Dummy-host reference: [`rails_app/config/initializers/command_tower.rb`](../rails_app/config/initializers/command_tower.rb).
|
|
39
|
+
|
|
40
|
+
### Email / SMTP
|
|
41
|
+
|
|
42
|
+
CommandTower owns ActionMailer delivery for engine mailers (email verification, password reset, messaging channel mail).
|
|
43
|
+
|
|
44
|
+
| Environment | Delivery |
|
|
45
|
+
|-------------|----------|
|
|
46
|
+
| `test` | `:test` (in-memory `ActionMailer::Base.deliveries`; no external SMTP) |
|
|
47
|
+
| development / production | `:smtp` unless the host explicitly sets `config.email.delivery_method` |
|
|
48
|
+
|
|
49
|
+
Non-secret knobs live on `config.email.*` (defaults: `smtp.gmail.com`, port `587`, `plain`, STARTTLS auto). Secrets come from Credential Resolution:
|
|
50
|
+
|
|
51
|
+
- `config.credentials.smtp.user_name` / `password`, or
|
|
52
|
+
- ENV `GMAIL_USER_NAME` / `GMAIL_PASSWORD`
|
|
53
|
+
|
|
54
|
+
`SmtpActionMailerBridge` merges resolved credentials into `action_mailer.smtp_settings` at the end of `CommandTower.configure`. Missing credentials fail at send when `raise_delivery_errors` is true. `From` uses the resolved SMTP username when present.
|
|
55
|
+
|
|
56
|
+
Doctor does **not** probe SMTP connectivity.
|
|
57
|
+
|
|
58
|
+
## Step 3 — Confirm mount path
|
|
59
|
+
|
|
60
|
+
Ensure routes include something like:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
mount CommandTower::Engine => "/" # or "/api"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Engine paths below are **relative to that mount**. Route area index: [Controllers](controllers.md).
|
|
67
|
+
|
|
68
|
+
## Step 3b — Inherit execution-boundary bases
|
|
69
|
+
|
|
70
|
+
Greenfield hosts should inherit CommandTower bases so Execution Context is established automatically:
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
class ApplicationController < CommandTower::ApplicationController
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class ApplicationJob < CommandTower::ApplicationJob
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Unauthenticated host endpoints (for example health checks) still receive HTTP `execution_uuid` / `correlation_id`. Successful authentication enriches the **same** context with `user_id` / `effective_user_id`.
|
|
81
|
+
|
|
82
|
+
Workflows and services consume `CommandTower::Current` (or `execution_context`); they do not establish a new execution. For Rake/console, use `CommandTower.with_execution(source: :rake) { ... }`.
|
|
83
|
+
|
|
84
|
+
`Auth::RequestContext` is JWT request/response transport, not Execution Context. Details: [Extending — Execution Context](extending.md#execution-context).
|
|
85
|
+
|
|
86
|
+
Lifecycle and semantic events: [Eventing](eventing.md).
|
|
87
|
+
|
|
88
|
+
If superclass inheritance is technically blocked, include `CommandTower::Execution::HttpBoundary` / `JobBoundary` on the host bases. That is an escape hatch, not the preferred contract.
|
|
89
|
+
|
|
90
|
+
## Step 4 — Host RBAC (required)
|
|
91
|
+
|
|
92
|
+
AuthorizeRequest **fails closed**. CommandTower ships **CT-owned** entity definitions (Me, session, inbox, Admin Workspace capabilities, …) and the platform full-access role (`owner`) in `lib/command_tower/authorization/default.yml`. CommandTower does **not** ship an operational `admin` role.
|
|
93
|
+
|
|
94
|
+
The host YAML (default `config/rbac_groups.yml`) is a **second source**. Composition is additive. Hosts:
|
|
95
|
+
|
|
96
|
+
1. Define product roles (typically `member`).
|
|
97
|
+
2. **Grant names** of already-defined CT entities to those roles.
|
|
98
|
+
3. Optionally define **host-owned** entities for **host** controllers.
|
|
99
|
+
4. Deliberately compose operational Admin roles (least privilege or a broad host-owned `admin`).
|
|
100
|
+
|
|
101
|
+
Do **not** copy CommandTower controller/entity blocks into the host file. Do **not** redefine `owner` or CT entity names. A host **may** define an `admin` group as host policy. Conflicts fail at boot (no last-write-wins).
|
|
102
|
+
|
|
103
|
+
Dummy host grants-only example: [`rails_app/config/rbac_groups.yml`](../rails_app/config/rbac_groups.yml).
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
CommandTower.configure do |c|
|
|
107
|
+
c.authorization.rbac_group_path = Rails.root.join("config/rbac_groups.yml")
|
|
108
|
+
c.authorization.default_membership_role = "member" # optional; nil disables
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`default_membership_role` is validated against the **composed** graph at boot. Unknown names fail configuration finalization.
|
|
113
|
+
|
|
114
|
+
More: [Authorization](authorization.md), [Authentication & authorization guide](authentication_authorization_guide.md).
|
|
115
|
+
|
|
116
|
+
Product audit names (later) register additively:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
CommandTower.configure do |c|
|
|
120
|
+
c.registry.audit.event :wager_placed do |event|
|
|
121
|
+
event.allowed_changes = %i[status]
|
|
122
|
+
event.user_history = true
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Do **not** redefine CommandTower-owned audit names. Audit rows persist in CommandTower after `command_tower:install:migrations` and `db:migrate`. See [Audit](audit.md).
|
|
128
|
+
|
|
129
|
+
Admin Workspace tools register additively (navigation metadata, not a dispatcher):
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
CommandTower.configure do |c|
|
|
133
|
+
c.registry.admin_workspace.tool :host_example do |tool|
|
|
134
|
+
tool.label = "Example"
|
|
135
|
+
tool.description = "Short launcher explanation of what this tool does."
|
|
136
|
+
tool.route = "/admin/example"
|
|
137
|
+
tool.group = :product
|
|
138
|
+
tool.sort_order = 300
|
|
139
|
+
tool.required_entity = :host_example_entity
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Do **not** redefine CommandTower-owned tool ids (`users`, `audit`, `messaging`). Hosts own copy for host tools (`description` soft ≤100 / hard ≤160). Manifest: [Admin Workspace](admin_workspace.md).
|
|
145
|
+
|
|
146
|
+
Principal capabilities register additively (frontend-projectable ids, not a dump of all entities):
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
CommandTower.configure do |c|
|
|
150
|
+
c.registry.principal_capabilities.capability :host_example do |capability|
|
|
151
|
+
capability.required_entity = :host_example_entity
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Do **not** redefine CommandTower-owned capability ids (`admin_workspace`, `admin_users`, `admin_impersonation`, `admin_audit_events`, `admin_messaging_announcements`). Projection: [Principal capabilities](principal_capabilities.md). Grant `admin_impersonation` only to roles that should start impersonation of visible users. Dummy `admin` does not include it.
|
|
157
|
+
|
|
158
|
+
## Step 5 — Roles on users
|
|
159
|
+
|
|
160
|
+
RBAC YAML defines what a role **may** do. Users still need that role assigned.
|
|
161
|
+
|
|
162
|
+
- Specs and the dummy pattern use role name **`member`** for Me/Auth surfaces (including entity `principal_capabilities`).
|
|
163
|
+
- When `authorization.default_membership_role` is set (for example `"member"`), register assigns that role **in the same transaction** as user create. Failure rolls back the user.
|
|
164
|
+
- When it is `nil`, register does not attach roles. Hosts may assign via product logic, ops (`command_tower:users:create`), or other host workflows.
|
|
165
|
+
- Installing CommandTower does **not** grant operational Admin access. Hosts must deliberately grant Admin entities.
|
|
166
|
+
- Admin announcements need a host role that includes `admin_messaging_announcements`.
|
|
167
|
+
- Admin Workspace manifest needs a host role that includes `admin_workspace`. Tool visibility still depends on each tool's `required_entity`.
|
|
168
|
+
- `GET /auth/principal-capabilities` needs entity `principal_capabilities` on the caller’s roles (grant on `member` like session/me). Possessed Admin projectables still depend on the Admin entity grants above.
|
|
169
|
+
- A host may define a broad `admin` role that grants many Admin entities — that is host policy, not a CommandTower default.
|
|
170
|
+
## Step 6 — Enable feature gates you need
|
|
171
|
+
|
|
172
|
+
When a gate is off, the route is **not drawn** → **404**.
|
|
173
|
+
|
|
174
|
+
| Capability | Config |
|
|
175
|
+
|------------|--------|
|
|
176
|
+
| Plain-text login | `config.login.plain_text.enable = true` |
|
|
177
|
+
| Email verification routes | `config.login.plain_text.email_verify = true` |
|
|
178
|
+
| Password reset routes | `config.login.plain_text.password_reset = true` |
|
|
179
|
+
| Email availability | `config.signup_session.email_availability = true` |
|
|
180
|
+
| Username availability | `config.username.realtime_username_check = true` |
|
|
181
|
+
|
|
182
|
+
Full gate list: [API reference — Feature gates](api_reference.md#feature-gates).
|
|
183
|
+
|
|
184
|
+
## Step 7 — Choose auth client path
|
|
185
|
+
|
|
186
|
+
- **API / mobile:** `Authorization: Bearer <jwt>` (default)
|
|
187
|
+
- **Browser / SPA cookies:** enable `config.jwt.cookie` (+ CSRF as needed) — [Cookie authentication](cookie_authentication_guide.md)
|
|
188
|
+
|
|
189
|
+
Engine HTTP success/error bodies use the application envelope `{ data, meta, errors }`. Host provisional helpers (`authenticate_user!`) can differ — [Authentication](authentication.md).
|
|
190
|
+
|
|
191
|
+
## Step 8 — Smoke check
|
|
192
|
+
|
|
193
|
+
With gates and RBAC in place (paths relative to your mount):
|
|
194
|
+
|
|
195
|
+
1. `POST /auth/register` (always drawn) — with `default_membership_role` configured, the user is created **and** assigned that role in one transaction.
|
|
196
|
+
2. `POST /auth/plain-text/login` (if enabled) — receive `data.token`.
|
|
197
|
+
3. `GET /me` with Bearer token — expect **200** and envelope `data` (account payload).
|
|
198
|
+
|
|
199
|
+
If you get **401**, authn failed. If you get **403**, fix Steps 4–5 before debugging clients.
|
|
200
|
+
|
|
201
|
+
Contracts: [API reference](api_reference.md).
|
|
202
|
+
|
|
203
|
+
## Step 9 — Messaging (when needed)
|
|
204
|
+
|
|
205
|
+
Host owns:
|
|
206
|
+
|
|
207
|
+
- Notification catalog / types
|
|
208
|
+
- `platform_enabled_channels` / channel policy
|
|
209
|
+
- Adapter credentials (email / SMS / Pushover)
|
|
210
|
+
|
|
211
|
+
Emit from a **host product workflow** via `Communications::Produce` / `ProduceMany` — not by bypassing workflows. See [Messaging](messaging_integration_guide.md) and [Extending](extending.md).
|
|
212
|
+
|
|
213
|
+
Phone/Pushover routes are always drawn; missing product readiness returns **503** capability errors.
|
|
214
|
+
|
|
215
|
+
## Step 10 — Host tests
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
require "command_tower/testing"
|
|
219
|
+
CommandTower::Testing.install!
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Details: [Testing](testing.md). Prefer `spec/requests/` patterns in the gem as HTTP contract proof.
|
|
223
|
+
|
|
224
|
+
## Done when
|
|
225
|
+
|
|
226
|
+
- Doctor passes for secrets/migrations
|
|
227
|
+
- Host `rbac_groups.yml` maps Me/Auth entities
|
|
228
|
+
- Users who should use Me surfaces have the host `member` (or equivalent) role
|
|
229
|
+
- Needed feature gates are enabled
|
|
230
|
+
- `GET /me` returns **200** with a Bearer token for a member user
|
|
231
|
+
|
|
232
|
+
## Common failures
|
|
233
|
+
|
|
234
|
+
| Symptom | Likely cause |
|
|
235
|
+
|---------|----------------|
|
|
236
|
+
| Doctor green, `GET /me` is **403** | Missing host RBAC YAML or user lacks `member` (Steps 4–5) |
|
|
237
|
+
| `POST /auth/plain-text/login` is **404** | `login.plain_text.enable?` off (Step 6) |
|
|
238
|
+
| Password-reset / availability **404** | Matching feature gate off |
|
|
239
|
+
| Phone / Pushover **503** | Capability / adapters not ready (Step 9) |
|
|
240
|
+
| Confused error JSON on host controllers | Provisional `authenticate_user!` vs engine envelope (Step 7) |
|
|
241
|
+
|
|
242
|
+
## Related
|
|
243
|
+
|
|
244
|
+
- [Initializing](initializing.md)
|
|
245
|
+
- [Extending](extending.md)
|
|
246
|
+
- [API reference](api_reference.md)
|
|
247
|
+
- [Authentication & authorization guide](authentication_authorization_guide.md)
|
|
248
|
+
- [Messaging](messaging_integration_guide.md)
|
|
249
|
+
- [README](../README.md)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Initializing CommandTower
|
|
2
|
+
|
|
3
|
+
CommandTower is a Rails engine. The install flow below gets the engine **mounted, migrated, and doctor-checked**. That alone is not a fully usable Me/Auth host — complete RBAC, feature gates, and a smoke check via the [Host integration guide](host_integration_guide.md).
|
|
4
|
+
|
|
5
|
+
Back to [README](../README.md).
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Gemfile: gem "command_tower" (or path/git source)
|
|
11
|
+
bundle install
|
|
12
|
+
|
|
13
|
+
bin/rails command_tower:install
|
|
14
|
+
bin/rails db:migrate
|
|
15
|
+
bin/rails command_tower:doctor
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`command_tower:install` does **not** run `db:migrate`. Installing and migrating stay separate on purpose.
|
|
19
|
+
|
|
20
|
+
### After install
|
|
21
|
+
|
|
22
|
+
Continue with [Host integration](host_integration_guide.md):
|
|
23
|
+
|
|
24
|
+
1. Host `rbac_groups.yml` product role that **grants** CT-owned Me/Auth entity names (required — otherwise authenticated Me/Auth calls **403**)
|
|
25
|
+
2. Set `authorization.default_membership_role` (for example `"member"`) or assign roles some other supported way
|
|
26
|
+
3. Enable feature gates you need (login, reset, availability, …)
|
|
27
|
+
4. Smoke-check `GET /me` with a Bearer token
|
|
28
|
+
5. Wire messaging catalog/adapters when you emit or use phone/Pushover
|
|
29
|
+
|
|
30
|
+
### What `command_tower:install` does
|
|
31
|
+
|
|
32
|
+
1. Runs Rails-native `command_tower:install:migrations` (copies engine migrations into the host `db/migrate/` with `*.command_tower.rb` scope).
|
|
33
|
+
2. Runs `rails g command_tower:configure` unless skipped (creates initializer; mounts the engine).
|
|
34
|
+
3. Prints next steps.
|
|
35
|
+
|
|
36
|
+
### Flags / environment
|
|
37
|
+
|
|
38
|
+
| Variable | Effect |
|
|
39
|
+
|----------|--------|
|
|
40
|
+
| `SKIP_CONFIGURE=1` | Migrations only (existing custom initializer / mount) |
|
|
41
|
+
| `SKIP_MOUNT=1` | Generate initializer but do not mount routes |
|
|
42
|
+
| `FORCE=1` | Overwrite an existing `config/initializers/command_tower.rb` |
|
|
43
|
+
|
|
44
|
+
Examples:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
SKIP_CONFIGURE=1 bin/rails command_tower:install
|
|
48
|
+
SKIP_MOUNT=1 bin/rails command_tower:install
|
|
49
|
+
FORCE=1 bin/rails command_tower:install
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configure generator (optional)
|
|
53
|
+
|
|
54
|
+
Prefer `command_tower:install` for greenfield hosts. The generator remains available:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bin/rails generate command_tower:configure
|
|
58
|
+
bin/rails generate command_tower:configure --skip-routes
|
|
59
|
+
bin/rails generate command_tower:configure --force
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The generator:
|
|
63
|
+
|
|
64
|
+
1. Adds `config/initializers/command_tower.rb` when missing (or overwrites with `--force`).
|
|
65
|
+
2. Mounts `CommandTower::Engine` unless `--skip-routes` or already mounted.
|
|
66
|
+
|
|
67
|
+
Dummy-host example initializer (engine development): [`rails_app/config/initializers/command_tower.rb`](../rails_app/config/initializers/command_tower.rb).
|
|
68
|
+
|
|
69
|
+
## Database migrations
|
|
70
|
+
|
|
71
|
+
CommandTower is the **sole authoring authority** for CommandTower-owned schema (`db/migrate` inside the gem). Hosts must **not** manually write, edit, or copy-paste CommandTower schema migrations (**AD-SCH-01**).
|
|
72
|
+
|
|
73
|
+
### Fresh host install
|
|
74
|
+
|
|
75
|
+
Handled by `command_tower:install` (step 1). Equivalent migration-only command:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bin/rails command_tower:install:migrations
|
|
79
|
+
bin/rails db:migrate
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`command_tower:install:migrations` is the standard Rails engine task (wrapper around `railties:install:migrations`). Re-running installation is **idempotent**: already-installed CommandTower migrations are skipped.
|
|
83
|
+
|
|
84
|
+
Rails may **retimestamp** newly copied host files. That is normal. Do not hand-edit installed `*.command_tower.rb` files.
|
|
85
|
+
|
|
86
|
+
### Upgrade workflow
|
|
87
|
+
|
|
88
|
+
1. Bump/release the CommandTower gem dependency in the host.
|
|
89
|
+
2. Run `bin/rails command_tower:install:migrations` (or `SKIP_CONFIGURE=1 bin/rails command_tower:install`).
|
|
90
|
+
3. Run `bin/rails db:migrate`.
|
|
91
|
+
4. Optionally run `bin/rails command_tower:doctor`.
|
|
92
|
+
|
|
93
|
+
Do not re-run configure on hosts that already customize the initializer unless you intend to regenerate it (`FORCE=1`).
|
|
94
|
+
|
|
95
|
+
### Adding future migrations
|
|
96
|
+
|
|
97
|
+
1. Author the migration **only** in CommandTower `db/migrate/`.
|
|
98
|
+
2. Release / bump the gem in the host.
|
|
99
|
+
3. Install + migrate as above.
|
|
100
|
+
|
|
101
|
+
Installed host copies are an **execution context**, not a second authoring home.
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
Required for production-ready hosts:
|
|
106
|
+
|
|
107
|
+
- `config.jwt.hmac_secret` — typically `SECRET_KEY_BASE` / `Rails.application.secret_key_base`
|
|
108
|
+
- `config.signup_session.jwt_secret` — or `SIGNUP_SESSION_JWT_SECRET`
|
|
109
|
+
- `config.password_recovery_session.jwt_secret` — or `PASSWORD_RECOVERY_SESSION_JWT_SECRET`
|
|
110
|
+
|
|
111
|
+
Optional / feature-gated:
|
|
112
|
+
|
|
113
|
+
- Messaging SMS / Pushover adapters and credential resolution (`config.credentials.*` or ENV)
|
|
114
|
+
- Cookie + CSRF JWT modes
|
|
115
|
+
- Admin enablement, application URL, welcome content hooks
|
|
116
|
+
|
|
117
|
+
The generated initializer documents available options via class_composer. Defaults live in CommandTower; do not duplicate them unnecessarily in the host.
|
|
118
|
+
|
|
119
|
+
## Doctor
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
bin/rails command_tower:doctor
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Checks Rails version compatibility, engine baseline migrations, host-installed migration copies, JWT / session secrets, and messaging adapter names. Failures abort with remediation text; warnings print but allow success.
|
|
126
|
+
|
|
127
|
+
Doctor does **not** probe Redis/SMTP connectivity.
|
|
128
|
+
|
|
129
|
+
## Host vs CommandTower responsibilities
|
|
130
|
+
|
|
131
|
+
| Concern | Owner |
|
|
132
|
+
|---------|--------|
|
|
133
|
+
| Author CT schema migrations | CommandTower |
|
|
134
|
+
| Install / run migrations | Host (via engine tasks + `db:migrate`) |
|
|
135
|
+
| Product configuration / secrets | Host initializer + ENV |
|
|
136
|
+
| Dual-author CT schema in the host | **Forbidden** |
|
|
137
|
+
|
|
138
|
+
## Troubleshooting
|
|
139
|
+
|
|
140
|
+
| Symptom | Fix |
|
|
141
|
+
|---------|-----|
|
|
142
|
+
| `users already exists` on migrate | Database not empty / stale `schema.rb` load — migrate an empty DB, or dump schema after a clean migrate |
|
|
143
|
+
| Install copies nothing new | Already installed — expected idempotency |
|
|
144
|
+
| Missing tables after install | You installed but did not `db:migrate` |
|
|
145
|
+
| Doctor fails on JWT default | Set `config.jwt.hmac_secret` from a real secret |
|
|
146
|
+
| Accidental public engine mount | Use `SKIP_MOUNT=1` / `--skip-routes`, or mount at the path your product needs |
|
|
147
|
+
|
|
148
|
+
## Existing customized hosts
|
|
149
|
+
|
|
150
|
+
If the host already has a bespoke initializer and mount:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
SKIP_CONFIGURE=1 bin/rails command_tower:install
|
|
154
|
+
# same as: bin/rails command_tower:install:migrations
|
|
155
|
+
bin/rails db:migrate
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Do not run `rails g command_tower:configure` unless you intentionally want stock scaffolding.
|
|
159
|
+
|
|
160
|
+
## Test suite factories
|
|
161
|
+
|
|
162
|
+
Shared FactoryBot definitions ship with the gem. In the host test boot path:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
require "command_tower/testing"
|
|
166
|
+
CommandTower::Testing.install!
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Then extend with `FactoryBot.modify` as needed. See [Testing with CommandTower](testing.md).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Messaging with CommandTower
|
|
2
|
+
|
|
3
|
+
CommandTower owns modern messaging **platform** surfaces. Hosts supply product content, channel policy, and adapter credentials.
|
|
4
|
+
|
|
5
|
+
Complete install and host RBAC first: [Host integration](host_integration_guide.md) (Steps 1–8 before emitting or wiring phone/Pushover).
|
|
6
|
+
|
|
7
|
+
## Current surfaces
|
|
8
|
+
|
|
9
|
+
| Concern | Path / entry |
|
|
10
|
+
|---------|----------------|
|
|
11
|
+
| User inbox (consume) | Engine `/me/inbox*` — see [API reference](api_reference.md#me-inbox) |
|
|
12
|
+
| Preferences | Engine `/me/preferences*` — see [API reference](api_reference.md#preferences) |
|
|
13
|
+
| Phone / Pushover endpoints | Engine `/me/phone*`, `/me/pushover*` (503 when capability unavailable) |
|
|
14
|
+
| Single-recipient emit | `CommandTower::Services::Messaging::Communications::Produce` |
|
|
15
|
+
| Multi-recipient emit | `CommandTower::Services::Messaging::Communications::ProduceMany` |
|
|
16
|
+
| Admin cohort announce | Engine `POST /admin/messaging/announcements` |
|
|
17
|
+
| Host ops announce | Host-owned rake/job (product-specific; not part of the gem) |
|
|
18
|
+
|
|
19
|
+
Authentication uses JWT bearer or cookie session as documented in [Authentication](authentication.md). Me Inbox authorization requires host RBAC entities (dummy host uses a `member` role mapping). Admin announcements use entity `admin_messaging_announcements` (engine `default.yml` admin group).
|
|
20
|
+
|
|
21
|
+
## Produce (single recipient)
|
|
22
|
+
|
|
23
|
+
Call from a **product workflow**, not from a controller service bypass:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
CommandTower::Services::Messaging::Communications::Produce.call(
|
|
27
|
+
user: user,
|
|
28
|
+
notification_type_key: "welcome",
|
|
29
|
+
host_event_identity: "welcome/#{user.id}/#{SecureRandom.uuid}",
|
|
30
|
+
title: "Welcome",
|
|
31
|
+
body: "Thanks for joining.",
|
|
32
|
+
platform_enabled_channels: [:inbox, :email],
|
|
33
|
+
metadata: { source: "onboarding" } # optional
|
|
34
|
+
)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Kwarg | Required |
|
|
38
|
+
|-------|----------|
|
|
39
|
+
| `user` | yes (`User`) |
|
|
40
|
+
| `notification_type_key` | yes (`String`) |
|
|
41
|
+
| `host_event_identity` | yes (`String`) |
|
|
42
|
+
| `title` | yes |
|
|
43
|
+
| `body` | yes |
|
|
44
|
+
| `platform_enabled_channels` | yes (`Array`) |
|
|
45
|
+
| `metadata` | no (`Hash`) |
|
|
46
|
+
|
|
47
|
+
## ProduceMany (multi recipient)
|
|
48
|
+
|
|
49
|
+
| Kwarg | Required / notes |
|
|
50
|
+
|-------|------------------|
|
|
51
|
+
| `user_ids` | yes (`Array` of integer ids) |
|
|
52
|
+
| `notification_type_key` | yes |
|
|
53
|
+
| `campaign_identity` | yes — fan-out builds `host_event_identity` as `"#{campaign_identity}/#{user.id}"` |
|
|
54
|
+
| `title`, `body` | yes |
|
|
55
|
+
| `platform_enabled_channels` | yes |
|
|
56
|
+
| `metadata` | optional |
|
|
57
|
+
| `execution_mode` | optional, default `:async`; `:sync` capped at **25** recipients |
|
|
58
|
+
|
|
59
|
+
Admin announcements HTTP is a product path over ProduceMany (async/sync, audience selection). Contract: [API reference — Admin messaging](api_reference.md#admin-messaging).
|
|
60
|
+
|
|
61
|
+
## Me Inbox HTTP (summary)
|
|
62
|
+
|
|
63
|
+
| Concern | Contract |
|
|
64
|
+
|---------|----------|
|
|
65
|
+
| List | `GET /me/inbox?limit=&offset=&scope=` — meta `{ limit, offset, totalCount }` |
|
|
66
|
+
| Detail / open / archive / delete | `/me/inbox/:id` (+ `open`, `archive`) |
|
|
67
|
+
| Bulk | `POST /me/inbox/bulk/{read,unread,archive,restore,delete}` with body `ids` |
|
|
68
|
+
| Unread | `GET /me/inbox/unread-count` |
|
|
69
|
+
|
|
70
|
+
Pagination detail: [pagination.md](pagination.md). Full catalog: [api_reference.md](api_reference.md#me-inbox).
|
|
71
|
+
|
|
72
|
+
## Host responsibilities
|
|
73
|
+
|
|
74
|
+
- Notification catalog content (registered into CommandTower notification types)
|
|
75
|
+
- `platform_enabled_channels` / channel policy injection
|
|
76
|
+
- Messaging adapter credentials (email / SMS / Pushover) via initializer or ENV
|
|
77
|
+
- Host product roles that grant CT-owned Me inbox/preferences/phone/pushover entities (and `admin` if used)
|
|
78
|
+
- Product-specific operational tooling (announce rake tasks, welcome copy)
|
|
79
|
+
|
|
80
|
+
## Related
|
|
81
|
+
|
|
82
|
+
- [Controllers / routes](controllers.md)
|
|
83
|
+
- [API reference](api_reference.md)
|
|
84
|
+
- [Extending](extending.md) — Produce from host workflows; Do-Not-Extend
|
|
85
|
+
- [Initializing](initializing.md) — configuration and doctor
|
|
86
|
+
- [Pagination](pagination.md)
|
|
87
|
+
- [README](../README.md)
|
data/docs/models.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# CommandTower Models
|
|
2
|
+
|
|
3
|
+
Core models live in the root / CommandTower namespaces and support authentication, authorization, and messaging ledger associations.
|
|
4
|
+
|
|
5
|
+
Back to [README](../README.md).
|
|
6
|
+
|
|
7
|
+
## User
|
|
8
|
+
|
|
9
|
+
`User` is the primary identity model for host applications.
|
|
10
|
+
|
|
11
|
+
It underpins [Authentication](authentication.md) and [Authorization](authorization.md), and participates in messaging via associations such as `messaging_communications`.
|
|
12
|
+
|
|
13
|
+
Sensitive session invalidation uses the user’s verifier token — see [Sensitive changes](sensitive_routes.md).
|
|
14
|
+
|
|
15
|
+
### Reopening `User` in the host
|
|
16
|
+
|
|
17
|
+
Prefer services/workflows for new behavior. When you must reopen the class:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
require CommandTower::Engine.root.join("app", "models", "user.rb")
|
|
21
|
+
|
|
22
|
+
class User
|
|
23
|
+
def self.my_class_method; end
|
|
24
|
+
def my_instance_method; end
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## UserSecret
|
|
29
|
+
|
|
30
|
+
`UserSecret` backs validation and recovery-related secrets (for example plain-text email validation flows). It complements [Sensitive changes](sensitive_routes.md).
|
|
31
|
+
|
|
32
|
+
### Reopening `UserSecret` in the host
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
require CommandTower::Engine.root.join("app", "models", "user_secret.rb")
|
|
36
|
+
|
|
37
|
+
class UserSecret
|
|
38
|
+
def self.my_class_method; end
|
|
39
|
+
def my_instance_method; end
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Messaging models
|
|
44
|
+
|
|
45
|
+
Engine-owned messaging persistence (communications, endpoints, preferences, and related records) ships with CommandTower. Prefer platform services/workflows for mutations; do not dual-author schema in the host ([Initializing](initializing.md)).
|
|
46
|
+
|
|
47
|
+
## Audit ledger
|
|
48
|
+
|
|
49
|
+
`CommandTower::Audit::Event` is the append-only platform ledger (`command_tower_audit_events`). Only `CommandTower::Audit::Persistence::Subscriber` writes rows. There are no foreign keys to users. Envelope `changes` persist as `change_set`. See [Audit](audit.md).
|
|
50
|
+
|
|
51
|
+
## Related
|
|
52
|
+
|
|
53
|
+
- [Architecture](architecture.md)
|
|
54
|
+
- [Sensitive changes](sensitive_routes.md)
|
|
55
|
+
- [Testing](testing.md) — shared factories for CT-owned models
|
data/docs/pagination.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Pagination
|
|
2
|
+
|
|
3
|
+
CommandTower exposes live HTTP list contracts that return pagination metadata: **Me Inbox**, **audit event lists** (`GET /me/audit-events`, `GET /admin/audit-events`), and **Admin Users** (`GET /admin/users`). These lists reuse the Inbox `limit` / `offset` / `totalCount` contract.
|
|
4
|
+
|
|
5
|
+
Back to [README](../README.md).
|
|
6
|
+
|
|
7
|
+
## Me Inbox list contract
|
|
8
|
+
|
|
9
|
+
`GET /me/inbox` accepts query parameters:
|
|
10
|
+
|
|
11
|
+
| Param | Default | Notes |
|
|
12
|
+
|-------|---------|--------|
|
|
13
|
+
| `limit` | `50` | Max `100` |
|
|
14
|
+
| `offset` | `0` | |
|
|
15
|
+
| `scope` | `"inbox"` | `"inbox"` or `"archived"` |
|
|
16
|
+
|
|
17
|
+
Success envelope `meta`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"limit": 50,
|
|
22
|
+
"offset": 0,
|
|
23
|
+
"totalCount": 123
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`data` is an array of inbox items. Full route table: [API reference — Me Inbox](api_reference.md#me-inbox).
|
|
28
|
+
|
|
29
|
+
Deserializer: `CommandTower::Deserializers::Messaging::Inbox::ListDeserializer`.
|
|
30
|
+
|
|
31
|
+
## Global pagination config
|
|
32
|
+
|
|
33
|
+
`CommandTower.config.pagination.limit` defaults to **10**. That value is a generic configuration default for pagination helpers — it is **not** the Me Inbox HTTP default (Inbox uses 50).
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
CommandTower.configure do |c|
|
|
37
|
+
c.pagination.limit = 50
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
There is no `pagination=true` / `page` / `cursor` query API on current engine HTTP surfaces. Admin audit list uses the same limit/offset contract as Inbox.
|
|
42
|
+
|
|
43
|
+
## Related
|
|
44
|
+
|
|
45
|
+
- [API reference](api_reference.md)
|
|
46
|
+
- [Messaging](messaging_integration_guide.md)
|
|
47
|
+
- [Controllers](controllers.md)
|