Module: Secvault

Extended by:
Secvault
Included in:
Secvault
Defined in:
lib/secvault.rb,
lib/secvault/railtie.rb,
lib/secvault/secrets.rb,
lib/secvault/version.rb,
lib/secvault/rails_secrets.rb

Overview

Secvault - Simple secrets management for Rails

Secvault restores the classic Rails secrets.yml functionality using simple, plain YAML files for environment-specific secrets management. Works consistently across all Rails versions.

Rails Version Support:

  • Rails 7.1+: Full compatibility with automatic setup
  • Rails 7.2+: Drop-in replacement for removed functionality
  • Rails 8.0+: Full compatibility

Quick Start:

Add this to an initializer:

# config/initializers/secvault.rb
Secvault.start!

Usage:

Rails.application.secrets.api_key
Rails.application.secrets.oauth_settings[:google_client_id]
Secvault.secrets.your_key  # Direct access
Rails::Secrets.load(env: 'development')  # Load default config/secrets.yml
Rails::Secrets.parse(['custom.yml'], env: Rails.env)  # Parse custom files

Getting Started:

1. Create config/secrets.yml with your secrets
2. Call Secvault.start! in an initializer
3. Use Rails.application.secrets.your_secret in your app
4. For production, use environment variables with ERB syntax

Defined Under Namespace

Classes: EarlyLoader, Error, RailsSecrets, Railtie, Secrets

Constant Summary collapse

VERSION =
"3.4.0"
@@loaded_secrets =

Internal storage for loaded secrets

nil

Instance Method Summary collapse

Instance Method Details

#active?Boolean

Check if Secvault is currently active (started)

Returns:



59
60
61
# File 'lib/secvault.rb', line 59

def active?
  @@loaded_secrets != nil
end

#install!Object



129
130
131
132
133
134
# File 'lib/secvault.rb', line 129

def install!
  return if defined?(Rails::Railtie).nil?

  require "secvault/railtie"
  require "secvault/rails_secrets"
end

#rails_integrated?Boolean

Check if Secvault is integrated with Rails.application.secrets

Returns:



64
65
66
# File 'lib/secvault.rb', line 64

def rails_integrated?
  defined?(Rails) && Rails::Secrets == Secvault::RailsSecrets
end

#secretsObject

Access to loaded secrets without Rails integration



54
55
56
# File 'lib/secvault.rb', line 54

def secrets
  @@loaded_secrets || ActiveSupport::OrderedOptions.new
end

#setup_early_application_secrets!(files: nil, application_class: nil) ⇒ Object Also known as: setup_early_secrets!

Early setup method for use in config/application.rb before other configuration This ensures Rails.application has secrets available during application class definition



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/secvault.rb', line 70

def setup_early_application_secrets!(files: nil, application_class: nil)
  return false unless defined?(Rails)

  # Default files if not provided
  files ||= begin
    default_files = ["config/secrets.yml"]

    # Add neeto-commons-backend file if available
    if defined?(NeetoCommonsBackend) && NeetoCommonsBackend.respond_to?(:shared_secrets_file)
      default_files.unshift(NeetoCommonsBackend.shared_secrets_file)
    end

    default_files
  end

  # Create a temporary Rails.application if it doesn't exist
  unless Rails.respond_to?(:application) && Rails.application
    # Create a temporary application-like object with secrets
    temp_app = Object.new

    # Add lazy secrets loading
    temp_app.define_singleton_method(:secrets) do
      @secrets ||= begin
        # Convert to full paths and filter existing files
        file_paths = files.map do |file|
          file.is_a?(Pathname) ? file : Rails.root.join(file)
        end.select(&:exist?)

        if file_paths.any?
          # Load secrets using Secvault
          all_secrets = Secvault::Secrets.parse(file_paths, env: Rails.env)
          current_secrets = ActiveSupport::OrderedOptions.new
          current_secrets.merge!(all_secrets)
          current_secrets
        else
          # Return empty secrets if no files found but include encryption structure
          secrets = ActiveSupport::OrderedOptions.new
          secrets.encryption = ActiveSupport::OrderedOptions.new
          secrets.encryption.primary_key = nil
          secrets.encryption.deterministic_key = nil
          secrets.encryption.key_derivation_salt = nil
          secrets
        end
      end
    end

    # Set up Rails.application to point to this temporary object
    Rails.define_singleton_method(:application) { temp_app }
  end

  true
rescue => e
  warn "[Secvault] Early application secrets setup failed: #{e.message}"
  false
end

#start!(files: [], integrate_with_rails: false, set_secret_key_base: true, hot_reload: (defined?(Rails) && Rails.env.respond_to?(:development?)) ? Rails.env.development? : false), logger: (defined?(Rails) && Rails.env.respond_to?(:production?)) ? !Rails.env.production? : true)) ⇒ Object

Start Secvault with simplified, unified API This is the main entry point for all Secvault functionality

Usage examples:

Secvault.start!                                    # Simple: config/secrets.yml + Rails integration
Secvault.start!(files: ['custom.yml'])            # Custom single file
Secvault.start!(files: ['base.yml', 'local.yml']) # Multiple files
Secvault.start!(integrate_with_rails: false)      # Load only, no Rails integration
Secvault.start!(hot_reload: true)                 # Enable hot reload in development

Access secrets:

Rails.application.secrets.your_key  # When integrate_rails: true (default)
Secvault.secrets.your_key           # Direct access (always available)

Options:

- files: Array of file paths (String or Pathname). Defaults to ['config/secrets.yml']
- integrate_with_rails: Integrate with Rails.application.secrets (default: false)
- set_secret_key_base: Set Rails.application.config.secret_key_base from secrets (default: true)
- hot_reload: Add reload_secrets! methods for development (default: true in development)
- logger: Enable logging (default: true except production)


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/secvault.rb', line 156

def start!(files: [], integrate_with_rails: false, set_secret_key_base: true,
  hot_reload: ((defined?(Rails) && Rails.env.respond_to?(:development?)) ? Rails.env.development? : false),
  logger: ((defined?(Rails) && Rails.env.respond_to?(:production?)) ? !Rails.env.production? : true))
  # Default to config/secrets.yml if no files specified
  files_to_load = files.empty? ? ["config/secrets.yml"] : Array(files)

  # Convert to Pathname objects and resolve relative to Rails.root
  file_paths = files_to_load.map do |file|
    file.is_a?(Pathname) ? file : Rails.root.join(file)
  end

  # Load secrets into Secvault.secrets
  load_secrets!(file_paths, logger: logger)

  # Integrate with Rails if requested
  if integrate_with_rails
    setup_rails_integration!(file_paths, set_secret_key_base: set_secret_key_base, logger: logger)
  end

  # Add hot reload functionality if requested
  if hot_reload
    add_hot_reload!(file_paths)
  end

  true
rescue => e
  Rails.logger&.error "[Secvault] Failed to start: #{e.message}" if defined?(Rails) && logger
  false
end