Rails main lets ActiveSupport::EncryptedFile try comma-separated keys when decrypting, including keys supplied through RAILS_MASTER_KEY.
This is unreleased Rails 8.2 development functionality; Rails 8.1.4 still expects a single key.
Successful fallback reads do not establish support for multi-key writes or a complete credentials-editing workflow.
Try fallback reads with a disposable file
This standalone example generates its own keys and uses a separate example variable.
It does not load application credentials or read RAILS_MASTER_KEY.
require "active_support"
require "active_support/encrypted_file"
require "tmpdir"
require "fileutils"
variable = "SAELOUN_SYNTHETIC_KEYS"
raise "Use an unset example variable" if ENV.key?(variable)
Dir.mktmpdir("fallback-read-") do |dir|
file_key, candidate_key = Array.new(2) do
ActiveSupport::EncryptedFile.generate_key
end
content_path = File.join(dir, "example.enc")
file = -> {
ActiveSupport::EncryptedFile.new(
content_path: content_path,
key_path: File.join(dir, "unused.key"),
env_key: variable,
raise_if_missing_key: true
)
}
begin
ENV[variable] = file_key
file.call.write("synthetic payload")
original = File.binread(content_path)
ENV[variable] = "#{candidate_key},#{file_key}"
puts file.call.read
begin
file.call.write("changed payload")
rescue ActiveSupport::EncryptedFile::InvalidKeyLengthError => error
puts error.class.name
end
puts File.binread(content_path) == original
ensure
ENV.delete(variable)
end
endOn the tested main revision, this prints synthetic payload, ActiveSupport::EncryptedFile::InvalidKeyLengthError, and true.
The first candidate cannot decrypt the file, so the reader tries the matching second key.
The attempted write fails, and the ciphertext remains unchanged.
On installed Active Support 8.1.2, the comma-separated read instead raises ArgumentError.
The write path still expects one key
The current encrypt method checks the length of the entire key string.
A comma-separated list therefore fails that check before the file is written, even if a read with the same list succeeded.
EncryptedFile#change also fails when changed contents must be written with that list.
This is not evidence that credentials:edit supports editing with multiple keys.
Input handling is limited too: candidates are split on commas without trimming whitespace.
All well-formed but incorrect keys produce ActiveSupport::MessageEncryptor::InvalidMessage; a malformed or empty first candidate can raise ArgumentError before a later valid key is tried.
Each factory call above creates a fresh object deliberately. The encryptor is cached per instance after first use, so changing the environment variable does not refresh an initialized object’s key set. This describes current reader behavior, not a production key-rotation procedure.
The example was tested with Ruby 3.4.5 and Rails main d0f264a using only disposable synthetic fixtures.
This differs from selecting credentials per environment.
Jeff Tsang introduced the change in Rails PR #59485.
See the pinned implementation, especially encrypt and encryptor, for the read/write distinction.
