{"id":13878217,"url":"https://github.com/svoop/dry-credentials","last_synced_at":"2025-10-28T10:06:57.255Z","repository":{"id":66851605,"uuid":"601970227","full_name":"svoop/dry-credentials","owner":"svoop","description":"A mixin to use encrypted credentials in your classes","archived":false,"fork":false,"pushed_at":"2025-01-05T19:17:28.000Z","size":105,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-13T21:01:53.629Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/svoop.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"svoop","custom":"https://donorbox.org/bitcetera"}},"created_at":"2023-02-15T08:10:32.000Z","updated_at":"2025-01-05T19:17:25.000Z","dependencies_parsed_at":"2023-02-21T05:00:37.110Z","dependency_job_id":"9e514689-ac39-4986-bc3b-3670e73c32ec","html_url":"https://github.com/svoop/dry-credentials","commit_stats":{"total_commits":17,"total_committers":1,"mean_commits":17.0,"dds":0.0,"last_synced_commit":"adc7faee08772f646e50be46cb0d0ebf7465099a"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/svoop/dry-credentials","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svoop%2Fdry-credentials","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svoop%2Fdry-credentials/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svoop%2Fdry-credentials/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svoop%2Fdry-credentials/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/svoop","download_url":"https://codeload.github.com/svoop/dry-credentials/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svoop%2Fdry-credentials/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260090496,"owners_count":22957241,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-06T08:01:43.021Z","updated_at":"2025-10-28T10:06:57.249Z","avatar_url":"https://github.com/svoop.png","language":"Ruby","funding_links":["https://github.com/sponsors/svoop","https://donorbox.org/bitcetera"],"categories":["Ruby"],"sub_categories":[],"readme":"[![Version](https://img.shields.io/gem/v/dry-credentials.svg?style=flat)](https://rubygems.org/gems/dry-credentials)\n[![Tests](https://img.shields.io/github/actions/workflow/status/svoop/dry-credentials/test.yml?style=flat\u0026label=tests)](https://github.com/svoop/dry-credentials/actions?workflow=Test)\n[![Code Climate](https://img.shields.io/codeclimate/maintainability/svoop/dry-credentials.svg?style=flat)](https://codeclimate.com/github/svoop/dry-credentials/)\n[![GitHub Sponsors](https://img.shields.io/github/sponsors/svoop.svg)](https://github.com/sponsors/svoop)\n\n# Dry::Credentials\n\nManage and deploy secrets (access keys, API tokens etc) in encrypted files which can safely be committed to the code repository. To decrypt and and use them, only one environment variable containing the corresponding key is required.\n\nWhile similar in purpose to ActiveSupport::EncryptedConfiguration, this lightweight implementation doesn't introduce any dependencies.\n\n* [Homepage](https://github.com/svoop/dry-credentials)\n* [API](https://www.rubydoc.info/gems/dry-credentials)\n* Author: [Sven Schwyn - Bitcetera](https://bitcetera.com)\n\nThank you for supporting free and open-source software by sponsoring on [GitHub](https://github.com/sponsors/svoop) or on [Donorbox](https://donorbox.com/bitcetera). Any gesture is appreciated, from a single Euro for a ☕️ cup of coffee to 🍹 early retirement.\n\n## Install\n\nAdd the following to the \u003ctt\u003eGemfile\u003c/tt\u003e or \u003ctt\u003egems.rb\u003c/tt\u003e of your [Bundler](https://bundler.io) powered Ruby project:\n\n```ruby\ngem 'dry-credentials'\n```\n\nAnd then install the bundle:\n\n```\nbundle install\n```\n\nSee [Integrations](#integrations) below for how to integrate Dry::Credentials into frameworks.\n\n## Usage\n\nExtend any class with `Dry::Credentials` to use the [default settings](#defaults):\n\n```ruby\nclass App\n  extend Dry::Credentials\nend\n```\n\nThe `credentials` macro allows you to tweak the settings:\n\n```ruby\nclass App\n  extend Dry::Credentials\n\n  credentials do\n    env \"sandbox\"\n    dir \"/path/to/credentials\"\n  end\nend\n```\n\n⚠️ The `dir` must exist and have the proper permissions set.\n\nNow initialize the credentials for this `env`:\n\n```ruby\nApp.credentials.edit!\n```\n\nIt creates `/path/to/credentials/sandbox.yml.enc` (where the encrypted credentials are stored) and opens this file using your favourite editor as per the `EDITOR` environment variable.\n\nFor the sake of this example, let's assume you paste the following credentials:\n\n```yml\notp:\n  secret_key: ZcikLNiUQoqOo594oH2eqw04HPclhjkpgvpBik/40oU=\n  salt: 583506a49c71724a9f085bf2e70362df9d973f08d6575191cab6a177dfb872c6\n  meta:\n    realm: main\n```\n\nWhen you close the editor, the credentials are encrypted and stored. This first time only, the key to encrypt and decrypt is printed to STDOUT:\n\n```\nSANDBOX_CREDENTIALS_KEY=68656973716a4e706e336733377245732b6e77584c6c772b5432446532456f674767664271374a623876383d\n```\n\n⚠️ In case you've entered invalid YAML, a warning will be printed and the editor reopens immediately.\n\nTo decrypt the credentials and use them in your app, you have to set just this one environment variable containing the key, in this case:\n\n```sh\nexport SANDBOX_CREDENTIALS_KEY=68656973716a4e706e336733377245732b6e77584c6c772b5432446532456f674767664271374a623876383d\n```\n\nAlternatively, you can omit the first part of the variable name. Such a key will be used for any app environment, but a more specific key will always take precedence. This is particularly useful when working with containerized setups:\n\n```sh\nexport CREDENTIALS_KEY=68656973716a4e706e336733377245732b6e77584c6c772b5432446532456f674767664271374a623876383d\n```\n\nWith this in place, you can use the decrypted credentials anywhere in your app:\n\n```ruby\nApp.credentials.otp.secret_key\n# =\u003e \"ZcikLNiUQoqOo594oH2eqw04HPclhjkpgvpBik/40oU=\"\n\nApp.credentials.otp.meta.realm\n# =\u003e \"main\"\n```\n\n## Environments\n\nCredentials are isolated into environments which most likely will, but don't necessarily have to align with the environments of the app framework you're using.\n\nBy default, the current environment is read from `APP_ENV`. You shouldn't use `RACK_ENV` for this, [here's why](https://github.com/rack/rack/issues/1546).\n\n⚠️ For safety reasons, don't share the same key across multiple environments!\n\n## Reload credentials\n\nThe credentials are lazy loaded when queried for the first time. After that, changes in the encrypted credentials files are not taken into account at runtime for efficiency reasons.\n\nHowever, you can schedule a reload:\n\n```ruby\nApp.credentials.reload!\n```\n\nThe reload is not done immediately but the next time credentials are queried.\n\n## Edit credentials\n\nThis gem does not provide any CLI tools to edit the credentials. You should integrate it into your app instead e.g. with a Rake task or an extension to the CLI tool of the app framework you're using.\n\nYou can explicitly pass the environment to edit:\n\n```ruby\nApp.credentials.edit! \"production\"\n```\n\nEditing credentials implicitly schedules a `reload!`.\n\n## Dynamic secrets\n\nIn case you have to partition secrets beyond environments, you can set dynamic secrets which are composed on the fly. Here's an example.\n\nYou want to be able to connect to a shared database for the test environment, but the database URL differs whether you run the tests locally or on your favourite CI platform. To differ between the two, you set an environment variable `CONTEXT` which is either `local` or `ci` and you defined the secrets accordingly:\n\n```yaml\ndatabase_url:\n  local: postgres://localhost:5432/example\n  ci: postgres://testuser:testpassword@remote.db.example.com:5432/example\n```\n\nTo get the actual database URL, you have to:\n\n```ruby\nApp.credentials.database_url.send(ENV['CONTEXT'])\n```\n\nThis is okay, but it may grow a lot longer and less readable in a real app. Enter dynamic secrets which are composed according to your needs:\n\n```ruby\nApp.credentials.define! :current_database_url do |credentials|\n  credentials.database_url.send(ENV['CONTEXT'])\nend\n```\n\nDynamic secrets are then available like any other secret, however, the block is called every time you query the dynamic secret:\n\n```ruby\nApp.credentials.current_database_url   # =\u003e \"postgres://localhost...\"\n```\n\n⚠️ Don't try to use the same key for a dynamic secret as for an existing regular one since this could create an endless loop and therefore any such attempt will raise a `Dry::Credentials::DefineError`.\n\n## Settings\n\nIf you have to, you can access the settings programmatically:\n\n```ruby\nApp.credentials[:env]   # =\u003e \"production\"\n```\n\n### Defaults\n\nSetting | Default | Description\n--------|---------|------------\n`env` | `-\u003e { ENV[\"APP_ENV\"] }` | environment such as `development`\n`dir` | `\"config/credentials\"` | directory where encrypted credentials are stored\n`cipher` | `\"aes-256-gcm\"` | any of `OpenSSL::Cipher.ciphers`\n`digest` | `\"sha256\"` | sign digest used if the cipher doesn't support AEAD\n`serializer` | `Marshal` | serializer responding to `dump` and `load`\n\n## Integrations\n\n### Bridgetown\n\nThe [bridgetown_credentials gem](https://github.com/svoop/bridgetown_credentials) integrates Dry::Credentials into your [Bridgetown](https://www.bridgetownrb.com) site.\n\n### Hanami 2\n\nTo use credentials in a [Hanami 2](https//hanami.org) app, first add this gem to the Gemfile of the app and then create a provider `config/providers/credentials.rb`:\n\n```ruby\n# frozen_string_literal: true\n\nHanami.app.register_provider :credentials do\n  prepare do\n    require \"dry-credentials\"\n  end\n\n  start do\n    Dry::Credentials::Extension.new.then do |credentials|\n      credentials[:env] = Hanami.env\n      credentials[:dir] = Hanami.app.root.join(credentials[:dir])\n      credentials[:dir].mkpath\n      credentials.load!\n      register \"credentials\", credentials\n    end\n  end\nend\n```\n\nNext up are Rake tasks `lib/tasks/credentials.rake`:\n\n```ruby\nnamespace :credentials do\n  desc \"Edit (or create) the encrypted credentials file\"\n  task :edit, [:env] =\u003e [:environment] do |_, args|\n    Hanami.app.prepare(:credentials)\n    Hanami.app['credentials'].edit! args[:env]\n  end\nend\n```\n\n(As of Hanami 2.1, you have to [explicitly load such tasks in the Rakefile](https://github.com/hanami/hanami/issues/1375) yourself.)\n\nYou can now create a new credentials file for the development environment:\n\n```\nrake credentials:edit\n```\n\nThis prints the credentials key you have to set in `.env`:\n\n```\nDEVELOPMENT_CREDENTIALS_KEY=...\n```\n\nThe credentials are now available anywhere you inject them:\n\n```ruby\nmodule MyHanamiApp\n  class ApiKeyPrinter\n    include Deps[\n      \"credentials\"\n    ]\n\n    def call\n      puts credentials.api_key\n    end\n  end\nend\n```\n\nYou can use the credentials in other providers. Say, you want to pass the [ROM](https://rom-rb.org/) database URL (which contains the connection password) using credentials instead of settings. Simply replace `target[\"settings\"].database_url` with `target[\"credentials\"].database_url` and you're good to go:\n\n```ruby\nHanami.app.register_provider :persistence, namespace: true do\n  prepare do\n    require \"rom\"\n\n    config = ROM::Configuration.new(:sql, target[\"credentials\"].database_url)\n\n    register \"config\", config\n    register \"db\", config.gateways[:default].connection\n  end\n\n  (...)\nend\n```\n\nFinally, if you have trouble using the credentials in slices, you might have to [share this app component](https://www.rubydoc.info/gems/hanami/Hanami/Config#shared_app_component_keys-instance_method) in `config/app.rb`:\n\n```ruby\nmodule MyHanamiApp\n  class App \u003c Hanami::App\n    config.shared_app_component_keys += [\"credentials\"]\n  end\nend\n```\n\n### Ruby on Rails\n\nActiveSupport implements [encrypted configuration](https://www.rubydoc.info/gems/activesupport/ActiveSupport/EncryptedConfiguration) which is used by `rails credentials:edit` [out of the box]((https://guides.rubyonrails.org/security.html#custom-credentials)). There not much benefit from introducing Dry::Credentials as an additional dependency.\n\n### Rodbot\n\nDry::Credentials is integrated into [Rodbot](https://github.com/svoop/rodbot) out of the box, see [the README for more](https://github.com/svoop/rodbot/blob/main/README.md#credentials).\n\n## Development\n\nTo install the development dependencies and then run the test suite:\n\n```\nbundle install\nbundle exec rake    # run tests once\nbundle exec guard   # run tests whenever files are modified\n```\n\nYou're welcome to join the [discussion forum](https://github.com/svoop/dry-credentials/discussions) to ask questions or drop feature ideas, [submit issues](https://github.com/svoop/dry-credentials/issues) you may encounter or contribute code by [forking this project and submitting pull requests](https://docs.github.com/en/get-started/quickstart/fork-a-repo).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvoop%2Fdry-credentials","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsvoop%2Fdry-credentials","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvoop%2Fdry-credentials/lists"}