# Cloak gives an Elixir app tagged ciphertexts and rotating keys

> Cloak is an Elixir encryption library built on Erlang's crypto, where vaults are modules you define in your own app, ciphertexts carry the tag of the algorithm and key that produced them, and an optional cloak_ecto package encrypts Ecto fields transparently. The interface is small, the key rotation story is the strong part, and the latest tagged release is from 2024.

**danielberkompas/cloak** — Elixir encryption library designed for Ecto

- Repository: https://github.com/danielberkompas/cloak
- Stars: 630 · Forks: 61
- Language: Elixir
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/danielberkompas-cloak

## A vault is a module in your own application

Cloak is an Elixir encryption library that implements several best practices and conveniences for Elixir developers, and the first one is structural. Cloak works through Vault modules which you define in your app, and you add them to your supervision tree. There is no global singleton and no application-wide configuration blob, which means the number of vaults you run is a design decision rather than a limitation. The documentation points out that you can have as many vaults as you wish running simultaneously in your project, and that this works well with umbrella apps, or with any runtime environment where multiple OTP apps use Cloak. That is the Elixir-native configuration the project advertises. The reason it matters is practical rather than aesthetic. A system that encrypts personal data for one tenant and operational data for another, or that needs a separate key for an environment that rotates on a different schedule, can express that as two supervised modules rather than as a branch in a config file.

## Tagged ciphertexts are what make rotation manageable

The feature the project leads with is tagged ciphertext. Each ciphertext contains metadata about the algorithm and key which was used to encrypt it, which allows Cloak to automatically select the correct key and algorithm to use for decryption for any given ciphertext. The stated payoff is key rotation, because you can easily tell whether any given ciphertext is using the old key or the new key. That is a real design decision rather than a convenience. A typical encryption library hands you an opaque blob and keeps its own bookkeeping about which key was used, which means the bookkeeping has to be stored next to the data, kept in sync, and migrated. Cloak moves that information into the ciphertext itself, so the rotation question becomes a lookup at decrypt time rather than a data migration. The tag is configured per cipher, and the built-in examples use tags of the form AES.GCM.V1 and AES.CTR.V1, which read like versioned names for a key, which is exactly the intent.

## Random IVs are generated for you

Every strong encryption algorithm recommends unique initialization vectors, and Cloak automatically generates unique vectors using :crypto.strong_rand_bytes, and includes the IV in the ciphertext. The README states plainly that this greatly simplifies storage and is not a security risk. That last clause is the one worth pausing on, because it is a claim about a design decision rather than about a property of AES. Storing the IV alongside the ciphertext is standard practice and is safe for the modes Cloak ships, since the IV is not secret and the risk comes from reusing an IV with the same key rather than from revealing it. The benefit is that the caller never has to think about it. A pipeline that encrypts a field does not need to generate an IV, attach it, and remember to attach it. The example in the README shows the shape of the result, with the encrypted value returned as an ok tuple holding a binary.

## Calling encrypt and decrypt from your own code

The API surface shown in the documentation is small. Encryption returns an ok tuple, and decryption takes the ciphertext and returns the plaintext back in the same shape.

```elixir
{:ok, ciphertext} = MyApp.Vault.encrypt("plaintext")

MyApp.Vault.decrypt(ciphertext)
# => {:ok, "plaintext"}
```

The bang variants exist as well, and the re-encryption example uses them to show a key or algorithm change end to end. The pipeline decrypts with the current key, then encrypts again, and the point of the example is that you can do that against a stored ciphertext without knowing which key it was encrypted with, because the tag tells the vault.

```elixir
"plaintext"
|> MyApp.Vault.encrypt!(:aes_256)
|> MyApp.Vault.decrypt!()
|> MyApp.Vault.encrypt!(:aes_256)
|> MyApp.Vault.decrypt!()
# => "plaintext"
```

So the intended workflow for a rotation is to add the new cipher to the configuration, re-encrypt the affected records through the vault, and let the tag on each ciphertext decide which key is needed at read time. The vault also exposes a bang form that raises rather than returning an error tuple, which is the usual Elixir convention for the cases where a failure is exceptional rather than expected.

## Configuring ciphers with an eye on interoperability

The configuration example shows what a vault module needs, a list of ciphers, each with a cipher module, a tag and a key. The GCM entry also sets an iv_length, and the comment above it is the most useful line in the file for anyone who will move data between systems.

```elixir
config :my_app, MyApp.Vault,
  ciphers: [
    aes_gcm: {Cloak.Ciphers.AES.GCM, tag: "AES.GCM.V1", key: <<...>>, iv_length: 12},
    aes_ctr: {Cloak.Ciphers.AES.CTR, tag: "AES.CTR.V1", key: <<...>>}
  ]
```

The comment says that in AES.GCM it is important to specify a 12-byte IV length for interoperability with other encryption software, and that in Cloak 2.0 this will be the default IV length for AES.GCM. The second cipher in the example is AES in CTR mode, which is the reminder that CTR is unauthenticated on its own. Cloak is built on Erlang's crypto library and therefore inherits its security, and the project also notes that you can implement your own cipher modules to use with Cloak, which may use any other encryption algorithms of your choice. That extension point is what makes the library usable in a regulated environment that has a mandated algorithm and a mandated IV policy.

## Ecto fields encrypted without changing the query layer

Cloak can be used to transparently encrypt Ecto fields, using the separate cloak_ecto package published on Hex. The split is worth noting. The core library has no Ecto dependency at all, so a project that stores encrypted values outside of Ecto, in a cache or a message queue, is not forced to take on the schema layer, and the dependency list in the core stays small. The cloak_ecto package then handles the schema side. For a team already using Ecto, this is the difference between encrypting values in the changeset and having the field come out of the database encrypted, and it is the part that makes the feature usable without auditing every call site. The repository layout reflects the separation, with lib/ holding the core, config/ holding the configuration, guides/ holding the documentation guides, and test/ and bin/ for the test suite.

## Where Cloak stops, and what the release history says

Three limits. The first is that the security story is inherited rather than argued. The README states that Cloak is built on Erlang's crypto library and therefore inherits its security, and the only example configuration shows AES in GCM and CTR modes. If your requirement is a specific authenticated encryption mode, a specific key derivation scheme, or a hardware key store, none of that is described in the documentation here, and the answer is to implement your own cipher module rather than to configure one. The second is that the key itself is configuration. A key written in a config file is a key that has to be delivered to the node somehow, and Cloak does not describe key management, key storage or a KMS integration, so that part of the system is yours. The third is the release record. The most recent tagged releases are v1.1.4, v1.1.3 and v1.1.2, all on 2024-04-06, so the last three published versions arrived within minutes of each other, and the last push to the repository was on 2026-10-16. That is a project that is still being worked on, with a version line that has not moved in a while, and the documentation mentions a 2.0 in the future where the GCM IV length changes by default. Anyone planning to upgrade across that boundary should read the upgrade guide the project links for the earlier 0.9 to 1.0 transition and expect a similar document to appear.

## Conclusion

Adopt Cloak if your Elixir application needs field level encryption with key rotation and you want the ciphertext to tell you which key produced it, because that tagging is what makes rotation a configuration change rather than a migration. Skip it if you need authenticated encryption with additional guarantees, because the default example shows AES in GCM and CTR modes and the README notes only that Cloak is built on Erlang's crypto and inherits its security. Before you configure a vault, read the AES.GCM comment about a 12-byte IV length, because interoperability with other encryption software depends on it, and pin the version you use since the last tagged release is v1.1.4 from 2024-04-06.

## FAQ

### What does Cloak do that a plain crypto call does not?

Cloak generates a unique IV for every encryption with :crypto.strong_rand_bytes and includes it in the ciphertext, and each ciphertext carries a tag describing the algorithm and key used, so the vault can pick the right key automatically at decryption time and key rotation becomes a configuration change rather than a data migration.

### How do I configure a Cloak vault?

You define a Vault module in your own application, add it to your supervision tree, and configure its ciphers with a cipher module, a tag and a key, for example aes_gcm: {Cloak.Ciphers.AES.GCM, tag: "AES.GCM.V1", key: <<...>>, iv_length: 12}. You can run as many vaults as you like at the same time.

### Can Cloak encrypt Ecto fields?

Yes, through the separate cloak_ecto package on Hex, which encrypts Ecto fields transparently. The core library itself has no Ecto dependency, so the schema integration is opt-in.

### What is the current release status of Cloak?

The most recent tagged releases are v1.1.4, v1.1.3 and v1.1.2, all dated 2024-04-06, and the last push to the repository was on 2026-10-16. The documentation also mentions Cloak 2.0, where the 12-byte AES.GCM IV length will become the default.

## Sources

- [danielberkompas/cloak on GitHub](https://github.com/danielberkompas/cloak)
- [Issues](https://github.com/danielberkompas/cloak/issues)
- [License: MIT](https://github.com/danielberkompas/cloak/blob/master/LICENSE)
- [README](https://github.com/danielberkompas/cloak/blob/master/README.md)
- [Releases](https://github.com/danielberkompas/cloak/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/danielberkompas-cloak
