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
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)
|
data/docs/controllers.md
ADDED
|
@@ -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/`.
|