From cc0c914f7fe8bebd08a7a99c8b58b33888421f48 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Nov 2025 14:37:32 +0000 Subject: [PATCH 1/6] Add SIGUSR1 signal handler to dump masked settings Adds signal handling for SIGUSR1 that dumps all application settings (both declared and dynamic) to Rails.log with sensitive values masked. - Creates new initializer config/initializers/signal_handlers.rb - Traps SIGUSR1 signal in both web (Puma) and worker (Sidekiq) processes - Dumps all declared Setting fields and dynamic Setting keys - Masks sensitive values (API keys, tokens, secrets, passwords) showing only first 4 characters - Logs to Rails.logger with process information and field counts - Runs in separate thread to avoid signal handler restrictions --- config/initializers/signal_handlers.rb | 91 ++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 config/initializers/signal_handlers.rb diff --git a/config/initializers/signal_handlers.rb b/config/initializers/signal_handlers.rb new file mode 100644 index 00000000000..4b6dcf3e94b --- /dev/null +++ b/config/initializers/signal_handlers.rb @@ -0,0 +1,91 @@ +# Signal handlers for web and worker processes +# +# SIGUSR1: Dump current settings array values (masked) to Rails.log +Rails.application.config.after_initialize do + # Helper lambda to mask sensitive values + mask_sensitive_value = lambda do |field_name, value| + return nil if value.nil? + + # Check if field name contains sensitive patterns + sensitive_patterns = [ + /key/i, + /token/i, + /secret/i, + /password/i, + /api/i, + /credentials?/i, + /auth/i + ] + + is_sensitive = sensitive_patterns.any? { |pattern| field_name.match?(pattern) } + + if is_sensitive && value.present? + case value + when String + # Show first 4 chars and mask the rest + if value.length <= 4 + "[MASKED]" + else + "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" + end + when TrueClass, FalseClass + value # Don't mask booleans + else + "[MASKED]" + end + else + value + end + end + + Signal.trap("USR1") do + # Run in a new thread to avoid signal handler restrictions + Thread.new do + begin + Rails.logger.info "=" * 80 + Rails.logger.info "SIGUSR1 received - Dumping application settings" + Rails.logger.info "Process: #{$PROGRAM_NAME} (PID: #{Process.pid})" + Rails.logger.info "=" * 80 + + # Get all declared fields from Setting model + # Find all field methods (getters) defined on the Setting singleton class + declared_fields = Setting.singleton_class.instance_methods(false) + .map(&:to_s) + .reject { |m| m.end_with?("=") || m.start_with?("raw_") || %w[[] []= key? delete dynamic_keys validate_onboarding_state! validate_openai_config!].include?(m) } + .sort + + # Get all dynamic fields + dynamic_fields = Setting.dynamic_keys.sort + + # Dump declared fields + unless declared_fields.empty? + Rails.logger.info "\n--- Declared Settings ---" + declared_fields.each do |field| + value = Setting.public_send(field) + masked_value = mask_sensitive_value.call(field, value) + Rails.logger.info " #{field}: #{masked_value.inspect}" + end + end + + # Dump dynamic fields + unless dynamic_fields.empty? + Rails.logger.info "\n--- Dynamic Settings ---" + dynamic_fields.each do |field| + value = Setting[field] + masked_value = mask_sensitive_value.call(field, value) + Rails.logger.info " #{field}: #{masked_value.inspect}" + end + end + + Rails.logger.info "\n" + "=" * 80 + Rails.logger.info "Settings dump complete (#{declared_fields.size} declared, #{dynamic_fields.size} dynamic)" + Rails.logger.info "=" * 80 + rescue => e + Rails.logger.error "Error dumping settings: #{e.class} - #{e.message}" + Rails.logger.error e.backtrace.join("\n") + end + end + end + + Rails.logger.info "Signal handlers initialized (SIGUSR1 -> dump settings)" +end From b4343223101ef263443f41d9f04db59c6422fd30 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Nov 2025 15:12:29 +0000 Subject: [PATCH 2/6] Fix SIGUSR1 signal handler for Puma web processes The previous implementation conflicted with Puma's use of SIGUSR1 for phased restarts, causing the web service to crash with SIGHUP-like behavior. Changes: - Move Puma-specific SIGUSR1 handler to config/puma.rb on_worker_boot - This sets up the handler in each Puma worker process after Puma's initialization, avoiding conflicts with the master process - Keep Sidekiq handler in initializer (no conflicts there) - Both web and worker services now properly dump settings on SIGUSR1 - Puma master can still use SIGUSR1 for other purposes if needed Usage: - Worker: kill -USR1 - Web: kill -USR1 --- config/initializers/signal_handlers.rb | 138 ++++++++++++------------- config/puma.rb | 73 +++++++++++++ 2 files changed, 137 insertions(+), 74 deletions(-) diff --git a/config/initializers/signal_handlers.rb b/config/initializers/signal_handlers.rb index 4b6dcf3e94b..f74831f9d9a 100644 --- a/config/initializers/signal_handlers.rb +++ b/config/initializers/signal_handlers.rb @@ -1,91 +1,81 @@ -# Signal handlers for web and worker processes +# Signal handlers for worker processes (Sidekiq) # # SIGUSR1: Dump current settings array values (masked) to Rails.log +# +# Note: For web processes (Puma), the signal handler is configured in config/puma.rb +# using on_worker_boot to avoid conflicts with Puma's master process signal handling Rails.application.config.after_initialize do - # Helper lambda to mask sensitive values - mask_sensitive_value = lambda do |field_name, value| - return nil if value.nil? - - # Check if field name contains sensitive patterns - sensitive_patterns = [ - /key/i, - /token/i, - /secret/i, - /password/i, - /api/i, - /credentials?/i, - /auth/i - ] + # Only set up signal handler for Sidekiq worker processes + # Puma workers get their handler set up in config/puma.rb + if defined?(Sidekiq) && Sidekiq.server? + Signal.trap("USR1") do + Thread.new do + begin + Rails.logger.info "=" * 80 + Rails.logger.info "SIGUSR1 received in Sidekiq worker - Dumping application settings" + Rails.logger.info "Process: #{$PROGRAM_NAME} (PID: #{Process.pid})" + Rails.logger.info "=" * 80 - is_sensitive = sensitive_patterns.any? { |pattern| field_name.match?(pattern) } + # Get all declared fields from Setting model + declared_fields = Setting.singleton_class.instance_methods(false) + .map(&:to_s) + .reject { |m| m.end_with?("=") || m.start_with?("raw_") || %w[[] []= key? delete dynamic_keys validate_onboarding_state! validate_openai_config!].include?(m) } + .sort - if is_sensitive && value.present? - case value - when String - # Show first 4 chars and mask the rest - if value.length <= 4 - "[MASKED]" - else - "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" - end - when TrueClass, FalseClass - value # Don't mask booleans - else - "[MASKED]" - end - else - value - end - end + # Get all dynamic fields + dynamic_fields = Setting.dynamic_keys.sort - Signal.trap("USR1") do - # Run in a new thread to avoid signal handler restrictions - Thread.new do - begin - Rails.logger.info "=" * 80 - Rails.logger.info "SIGUSR1 received - Dumping application settings" - Rails.logger.info "Process: #{$PROGRAM_NAME} (PID: #{Process.pid})" - Rails.logger.info "=" * 80 + # Helper to mask sensitive values + mask_value = lambda do |field_name, value| + return nil if value.nil? - # Get all declared fields from Setting model - # Find all field methods (getters) defined on the Setting singleton class - declared_fields = Setting.singleton_class.instance_methods(false) - .map(&:to_s) - .reject { |m| m.end_with?("=") || m.start_with?("raw_") || %w[[] []= key? delete dynamic_keys validate_onboarding_state! validate_openai_config!].include?(m) } - .sort + sensitive = [/key/i, /token/i, /secret/i, /password/i, /api/i, /credentials?/i, /auth/i] + is_sensitive = sensitive.any? { |pattern| field_name.match?(pattern) } - # Get all dynamic fields - dynamic_fields = Setting.dynamic_keys.sort + if is_sensitive && value.present? + case value + when String + value.length <= 4 ? "[MASKED]" : "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" + when TrueClass, FalseClass + value + else + "[MASKED]" + end + else + value + end + end - # Dump declared fields - unless declared_fields.empty? - Rails.logger.info "\n--- Declared Settings ---" - declared_fields.each do |field| - value = Setting.public_send(field) - masked_value = mask_sensitive_value.call(field, value) - Rails.logger.info " #{field}: #{masked_value.inspect}" + # Dump declared fields + unless declared_fields.empty? + Rails.logger.info "\n--- Declared Settings ---" + declared_fields.each do |field| + value = Setting.public_send(field) + masked_value = mask_value.call(field, value) + Rails.logger.info " #{field}: #{masked_value.inspect}" + end end - end - # Dump dynamic fields - unless dynamic_fields.empty? - Rails.logger.info "\n--- Dynamic Settings ---" - dynamic_fields.each do |field| - value = Setting[field] - masked_value = mask_sensitive_value.call(field, value) - Rails.logger.info " #{field}: #{masked_value.inspect}" + # Dump dynamic fields + unless dynamic_fields.empty? + Rails.logger.info "\n--- Dynamic Settings ---" + dynamic_fields.each do |field| + value = Setting[field] + masked_value = mask_value.call(field, value) + Rails.logger.info " #{field}: #{masked_value.inspect}" + end end - end - Rails.logger.info "\n" + "=" * 80 - Rails.logger.info "Settings dump complete (#{declared_fields.size} declared, #{dynamic_fields.size} dynamic)" - Rails.logger.info "=" * 80 - rescue => e - Rails.logger.error "Error dumping settings: #{e.class} - #{e.message}" - Rails.logger.error e.backtrace.join("\n") + Rails.logger.info "\n" + "=" * 80 + Rails.logger.info "Settings dump complete (#{declared_fields.size} declared, #{dynamic_fields.size} dynamic)" + Rails.logger.info "=" * 80 + rescue => e + Rails.logger.error "Error dumping settings: #{e.class} - #{e.message}" + Rails.logger.error e.backtrace.join("\n") + end end end - end - Rails.logger.info "Signal handlers initialized (SIGUSR1 -> dump settings)" + Rails.logger.info "Signal handlers initialized for Sidekiq worker (SIGUSR1 -> dump settings)" + end end diff --git a/config/puma.rb b/config/puma.rb index 47a2362e275..d7d3869f6c9 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -54,3 +54,76 @@ # isn't killed by Puma when suspended by a debugger. worker_timeout 3600 end + +# Set up SIGUSR1 handler in worker processes to dump settings +# This runs after Puma sets up its worker, so it only affects individual workers +# The master process can still use SIGUSR1 for phased restarts +on_worker_boot do + Signal.trap("USR1") do + Thread.new do + begin + Rails.logger.info "=" * 80 + Rails.logger.info "SIGUSR1 received in Puma worker - Dumping application settings" + Rails.logger.info "Process: #{$PROGRAM_NAME} (PID: #{Process.pid})" + Rails.logger.info "=" * 80 + + # Get all declared fields from Setting model + declared_fields = Setting.singleton_class.instance_methods(false) + .map(&:to_s) + .reject { |m| m.end_with?("=") || m.start_with?("raw_") || %w[[] []= key? delete dynamic_keys validate_onboarding_state! validate_openai_config!].include?(m) } + .sort + + # Get all dynamic fields + dynamic_fields = Setting.dynamic_keys.sort + + # Helper to mask sensitive values + mask_value = lambda do |field_name, value| + return nil if value.nil? + + sensitive = [/key/i, /token/i, /secret/i, /password/i, /api/i, /credentials?/i, /auth/i] + is_sensitive = sensitive.any? { |pattern| field_name.match?(pattern) } + + if is_sensitive && value.present? + case value + when String + value.length <= 4 ? "[MASKED]" : "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" + when TrueClass, FalseClass + value + else + "[MASKED]" + end + else + value + end + end + + # Dump declared fields + unless declared_fields.empty? + Rails.logger.info "\n--- Declared Settings ---" + declared_fields.each do |field| + value = Setting.public_send(field) + masked_value = mask_value.call(field, value) + Rails.logger.info " #{field}: #{masked_value.inspect}" + end + end + + # Dump dynamic fields + unless dynamic_fields.empty? + Rails.logger.info "\n--- Dynamic Settings ---" + dynamic_fields.each do |field| + value = Setting[field] + masked_value = mask_value.call(field, value) + Rails.logger.info " #{field}: #{masked_value.inspect}" + end + end + + Rails.logger.info "\n" + "=" * 80 + Rails.logger.info "Settings dump complete (#{declared_fields.size} declared, #{dynamic_fields.size} dynamic)" + Rails.logger.info "=" * 80 + rescue => e + Rails.logger.error "Error dumping settings: #{e.class} - #{e.message}" + Rails.logger.error e.backtrace.join("\n") + end + end + end +end From ccf34fd0710be408c25892b58f01196a93e12660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Mata?= Date: Mon, 17 Nov 2025 16:57:52 +0000 Subject: [PATCH 3/6] Linter noise --- config/initializers/signal_handlers.rb | 4 ++-- config/puma.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/initializers/signal_handlers.rb b/config/initializers/signal_handlers.rb index f74831f9d9a..681c1334e16 100644 --- a/config/initializers/signal_handlers.rb +++ b/config/initializers/signal_handlers.rb @@ -29,13 +29,13 @@ mask_value = lambda do |field_name, value| return nil if value.nil? - sensitive = [/key/i, /token/i, /secret/i, /password/i, /api/i, /credentials?/i, /auth/i] + sensitive = [ /key/i, /token/i, /secret/i, /password/i, /api/i, /credentials?/i, /auth/i ] is_sensitive = sensitive.any? { |pattern| field_name.match?(pattern) } if is_sensitive && value.present? case value when String - value.length <= 4 ? "[MASKED]" : "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" + value.length <= 4 ? "[MASKED]" : "#{value[0..3]}#{'*' * [ value.length - 4, 8 ].min}" when TrueClass, FalseClass value else diff --git a/config/puma.rb b/config/puma.rb index d7d3869f6c9..92f1ea5c9a9 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -80,13 +80,13 @@ mask_value = lambda do |field_name, value| return nil if value.nil? - sensitive = [/key/i, /token/i, /secret/i, /password/i, /api/i, /credentials?/i, /auth/i] + sensitive = [ /key/i, /token/i, /secret/i, /password/i, /api/i, /credentials?/i, /auth/i ] is_sensitive = sensitive.any? { |pattern| field_name.match?(pattern) } if is_sensitive && value.present? case value when String - value.length <= 4 ? "[MASKED]" : "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" + value.length <= 4 ? "[MASKED]" : "#{value[0..3]}#{'*' * [ value.length - 4, 8 ].min}" when TrueClass, FalseClass value else From 6828d83f651b11856f9a8ba7a6ab04f965ccabdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Mata?= Date: Mon, 17 Nov 2025 17:49:47 +0000 Subject: [PATCH 4/6] DRY comments --- config/initializers/sidekiq.rb | 5 ++ config/puma.rb | 70 +------------------- lib/settings_log_dump.rb | 113 +++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 67 deletions(-) create mode 100644 lib/settings_log_dump.rb diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index b80a8fddf23..d82d66cc169 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,4 +1,5 @@ require "sidekiq/web" +require Rails.root.join("lib/settings_signal_dumper") if Rails.env.production? Sidekiq::Web.use(Rack::Auth::Basic) do |username, password| @@ -14,3 +15,7 @@ # 10 min "catch-up" window in case worker process is re-deploying when cron tick occurs config.reschedule_grace_period = 600 end + +Sidekiq.configure_server do |_config| + SettingsLogDump.install_usr1_trap(process_label: "Sidekiq worker") +end diff --git a/config/puma.rb b/config/puma.rb index 92f1ea5c9a9..b2bd19f0ca6 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -2,6 +2,8 @@ # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. +require_relative "../lib/settings_log_dump" + rails_env = ENV.fetch("RAILS_ENV", "development") # Puma starts a configurable number of processes (workers) and each process @@ -59,71 +61,5 @@ # This runs after Puma sets up its worker, so it only affects individual workers # The master process can still use SIGUSR1 for phased restarts on_worker_boot do - Signal.trap("USR1") do - Thread.new do - begin - Rails.logger.info "=" * 80 - Rails.logger.info "SIGUSR1 received in Puma worker - Dumping application settings" - Rails.logger.info "Process: #{$PROGRAM_NAME} (PID: #{Process.pid})" - Rails.logger.info "=" * 80 - - # Get all declared fields from Setting model - declared_fields = Setting.singleton_class.instance_methods(false) - .map(&:to_s) - .reject { |m| m.end_with?("=") || m.start_with?("raw_") || %w[[] []= key? delete dynamic_keys validate_onboarding_state! validate_openai_config!].include?(m) } - .sort - - # Get all dynamic fields - dynamic_fields = Setting.dynamic_keys.sort - - # Helper to mask sensitive values - mask_value = lambda do |field_name, value| - return nil if value.nil? - - sensitive = [ /key/i, /token/i, /secret/i, /password/i, /api/i, /credentials?/i, /auth/i ] - is_sensitive = sensitive.any? { |pattern| field_name.match?(pattern) } - - if is_sensitive && value.present? - case value - when String - value.length <= 4 ? "[MASKED]" : "#{value[0..3]}#{'*' * [ value.length - 4, 8 ].min}" - when TrueClass, FalseClass - value - else - "[MASKED]" - end - else - value - end - end - - # Dump declared fields - unless declared_fields.empty? - Rails.logger.info "\n--- Declared Settings ---" - declared_fields.each do |field| - value = Setting.public_send(field) - masked_value = mask_value.call(field, value) - Rails.logger.info " #{field}: #{masked_value.inspect}" - end - end - - # Dump dynamic fields - unless dynamic_fields.empty? - Rails.logger.info "\n--- Dynamic Settings ---" - dynamic_fields.each do |field| - value = Setting[field] - masked_value = mask_value.call(field, value) - Rails.logger.info " #{field}: #{masked_value.inspect}" - end - end - - Rails.logger.info "\n" + "=" * 80 - Rails.logger.info "Settings dump complete (#{declared_fields.size} declared, #{dynamic_fields.size} dynamic)" - Rails.logger.info "=" * 80 - rescue => e - Rails.logger.error "Error dumping settings: #{e.class} - #{e.message}" - Rails.logger.error e.backtrace.join("\n") - end - end - end + SettingsLogDump.install_usr1_trap(process_label: "Puma worker") end diff --git a/lib/settings_log_dump.rb b/lib/settings_log_dump.rb new file mode 100644 index 00000000000..b639875f1d9 --- /dev/null +++ b/lib/settings_log_dump.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require "timeout" + +module SettingsSignalDumper + SENSITIVE_PATTERNS = [ + /key/i, + /token/i, + /secret/i, + /password/i, + /api/i, + /credentials?/i, + /auth/i + ].freeze + + SETTINGS_QUERY_TIMEOUT = 5 + + def self.install_usr1_trap(process_label:, logger: nil) + logger ||= Rails.logger + + Signal.trap("USR1") do + Thread.new do + dump_settings(process_label: process_label, logger: logger) + end + end + + logger.info "Signal handler initialized for #{process_label} (SIGUSR1 -> dump settings)" + end + + def self.dump_settings(process_label:, logger: nil) + logger ||= Rails.logger + + declared_fields = declared_setting_fields + dynamic_fields = dynamic_setting_fields(logger: logger) + + logger.info "=" * 80 + logger.info "SIGUSR1 received in #{process_label} - Dumping application settings" + logger.info "Process: #{$PROGRAM_NAME} (PID: #{Process.pid})" + logger.info "=" * 80 + + unless declared_fields.empty? + logger.info "\n--- Declared Settings ---" + declared_fields.each do |field| + value = Setting.public_send(field) + masked_value = mask_value(field, value) + logger.info " #{field}: #{masked_value.inspect}" + end + end + + unless dynamic_fields.empty? + logger.info "\n--- Dynamic Settings ---" + dynamic_fields.each do |field| + value = Setting[field] + masked_value = mask_value(field, value) + logger.info " #{field}: #{masked_value.inspect}" + end + end + + logger.info "\n" + "=" * 80 + logger.info "Settings dump complete (#{declared_fields.size} declared, #{dynamic_fields.size} dynamic)" + logger.info "=" * 80 + rescue => e + logger.error "Error dumping settings: #{e.class} - #{e.message}" + logger.error e.backtrace.join("\n") + end + + def self.declared_setting_fields + Setting.singleton_class.instance_methods(false) + .map(&:to_s) + .reject do |method_name| + method_name.end_with?("=") || + method_name.start_with?("raw_") || + %w[[] []= key? delete dynamic_keys validate_onboarding_state! validate_openai_config!].include?(method_name) + end + .sort + end + + def self.dynamic_setting_fields(logger: nil) + logger ||= Rails.logger + + Timeout.timeout(SETTINGS_QUERY_TIMEOUT) do + Setting.dynamic_keys.sort + end + rescue Timeout::Error + logger.error "Timed out fetching dynamic settings after #{SETTINGS_QUERY_TIMEOUT} seconds" + [] + end + + def self.mask_value(field_name, value) + return nil if value.nil? + + is_sensitive = SENSITIVE_PATTERNS.any? { |pattern| field_name.match?(pattern) } + + if is_sensitive + case value + when String + if value.empty? + "[EMPTY]" + elsif value.length <= 4 + "[MASKED]" + else + "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" + end + when TrueClass, FalseClass + value + else + "[MASKED]" + end + else + value + end + end +end From b1fd3e6d1c5eb5f55eefebb3cd4d329a75c918f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Mata?= Date: Mon, 17 Nov 2025 17:51:31 +0000 Subject: [PATCH 5/6] Linter --- lib/settings_log_dump.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/settings_log_dump.rb b/lib/settings_log_dump.rb index b639875f1d9..e16a9cd5694 100644 --- a/lib/settings_log_dump.rb +++ b/lib/settings_log_dump.rb @@ -99,7 +99,7 @@ def self.mask_value(field_name, value) elsif value.length <= 4 "[MASKED]" else - "#{value[0..3]}#{'*' * [value.length - 4, 8].min}" + "#{value[0..3]}#{'*' * [ value.length - 4, 8 ].min}" end when TrueClass, FalseClass value From bf173f846a8e37a2e9bfda85415225bdb7290ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Jos=C3=A9=20Mata?= Date: Mon, 17 Nov 2025 17:58:18 +0000 Subject: [PATCH 6/6] Filename mis-match --- config/initializers/sidekiq.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index d82d66cc169..ec4900fcdbf 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,5 +1,5 @@ require "sidekiq/web" -require Rails.root.join("lib/settings_signal_dumper") +require Rails.root.join("lib/settings_log_dump") if Rails.env.production? Sidekiq::Web.use(Rack::Auth::Basic) do |username, password|