Encrypted Runtime Settings for Ruby and Elixir
Tags: ruby, elixir, rails, phoenix
Published: 2021-03-03 18:43:38

---
title: Encrypted Runtime Settings for Ruby and Elixir
published: true
description: SharedSettings is a collection of libraries to manage runtime settings for either Ruby or Elixir alone or together
tags: ruby, elixir, rails, phoenix
cover_image: https://raw.githubusercontent.com/kieraneglin/shared-settings-ui-ex/master/assets/main_interface.png
---

_This will both be an introduction to the library as well as a guide for setting up SharedSettings on Phoenix and Rails.  If you don't care about the motivation behind SharedSettings, you can safely skip to the next section._

While migrating some services from Rails to Phoenix, we had a need to keep some settings in-sync between apps.  There was a system of uploading secrets to a secure location which the apps would consume, but the new secrets wouldn't take effect until next app boot (read: deploy).

After some hassle with this old process, I decided to create a library that would allow for simple management of runtime settings while keeping both apps in-sync. Once a proof of concept was completed, I realized that this was also useful for controlling standalone apps. That's why I've released SharedSettings as an [Elixir package](https://github.com/kieraneglin/shared-settings-ex), a [Ruby Gem](https://github.com/kieraneglin/shared-settings-rb), and a [UI library](https://github.com/kieraneglin/shared-settings-ui).

I created SharedSettings with these goals in mind:

1. Simple - API should be straightforward with an interface non-devs could use
2. Extendable - it should be easy to create new storage adapters or port SharedSettings to other languages
3. Secure - it should be able to store secrets if needed

## Usage

Elixir <> Ruby interop with encryption and UI will be covered in this section, but this will still be valuable if you only care about setting up one app.

### Prerequisites

- A Phoenix project
- A Rails project
- redis running locally

### Phoenix

[Package link](https://github.com/kieraneglin/shared-settings-ex)

Phoenix will be configured to use SharedSettings, but it will also be the host of the SharedSettings UI. If you are using Elixir <> Ruby interop, it's recommended that the UI is hosted on Elixir.

To get started, add the following to your `mix.exs`:

```elixir
{:redix, "~> 0.9"},
{:shared_settings, "~> 0.2.0"},
{:shared_settings_ui, "~> 0.2.0"}
```

Once you've fetched those deps, we need to add some `shared_settings` configuration to `config.exs` (or similar):

```elixir
config :shared_settings,
  cache_ttl: 300, # Set to 0 to disable
  cache_adapter: SharedSettings.Cache.EtsStore,
  storage_adapter: SharedSettings.Persistence.Redis,
  encryption_key: "...",
  redis: [ # Can also take a URI string
    host: "localhost",
    port: 6379,
    database: 0
  ]
```

As you see, we need to provide an encryption key if we want to encrypt settings.  This is optional, but for the purpose of this guide we'll generate a key with `mix SharedSettings.CreateKey`. Make sure you keep this key in a secure place and don't check it into version control!

We're almost done!  If you wanted to use SharedSettings without UI, this is where you'd stop.  Since we want the UI, we add the following to our `router.ex`:

```elixir
  scope path: "/shared-settings" do
    # Namespace must be the full route's namespace with no leading or trailing slash
    forward "/", SharedSettings.UI.Router, namespace: "shared-settings"
  end
```

**As it stands, this UI would be accessible to the outside world.**  Security is up to you and can be as simple as putting this through a pipeline with basic auth.

That's it!  You can see the dashboard at `/shared-settings` and use it to create, update, and destroy settings.  Use `SharedSettings.get/1` within your application code to access your settings (see the docs for more).

### Rails

[Gem link](https://github.com/kieraneglin/shared-settings-rb)

Since the UI is set up on Elixir, we only have to concern ourselves with setting up the core library.  You only have to configure the UI library once.

To start, add the following to your Gemfile:

```ruby
gem 'redis', '~> 4.0'
gem 'shared-settings'
```

Next, create an initializer at `config/initializers/shared_settings.rb` with the following contents:

```ruby
@client = Redis.new # Points to same Redis instance used by Elixir
@adapter = SharedSettings::Persistence::Redis.new(@client)

SharedSettings.configure do |config|
  config.default { SharedSettings.new(@adapter) }
  config.encryption_key = '...' # Same key that's used in Elixir
end
```

That's it! Now any setting created by either app can also be read by either app.  Encrypted settings are accessible by either app and the UI can be used to manage it all.

### Extending

If you want to use something other than Redis for storage, that's easy to do!  Check out the `Store` behaviour in the Elixir app to see how to create an interface for something like Postgres or Mongo.  If you're using only Elixir, a DETS adapter would be a great fit to keep your dependencies down.