diff --git a/README.md b/README.md
index a78468a5..53cfbf2e 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,8 @@ Welcome! Here you will find everything you need to know to get started with and
## How-to
-- [Optimistic Locking](documentation/how-to/cook-book/preventing-concurrent-writes.livemd)
+- [Encrypt Attributes](documentation/how-to/encrypt-attributes.livemd)
+- [Prevent Concurrent Writes](documentation/how-to/prevent-concurrent-writes.livemd)
---
diff --git a/documentation/assets/livebooks.css b/documentation/assets/livebooks.css
new file mode 100644
index 00000000..6a53add7
--- /dev/null
+++ b/documentation/assets/livebooks.css
@@ -0,0 +1,3 @@
+.livebook-badge-container + pre {
+ display: none;
+}
diff --git a/documentation/how-to/encrypt-attributes.livemd b/documentation/how-to/encrypt-attributes.livemd
new file mode 100644
index 00000000..7299445f
--- /dev/null
+++ b/documentation/how-to/encrypt-attributes.livemd
@@ -0,0 +1,134 @@
+
+
+# Encrypt Attributes
+
+```elixir
+Mix.install([{:ash, "~> 3.0.0-rc"}, {:ash_cloak, "~> 0.1.0-rc"}, {:cloak, "~> 1.1"}],
+ consolidate_protocols: false
+)
+
+Application.put_env(:my_app, MyApp.Vault,
+ ciphers: [
+ default: {
+ Cloak.Ciphers.AES.GCM,
+ tag: "AES.GCM.V1",
+ key: Base.decode64!("ETpvtowVAL7JmcxfqJ+XVQWzKrt1ynAkC0vT7AxfyNU="),
+ iv_length: 12
+ }
+ ]
+)
+
+defmodule MyApp.Vault do
+ use Cloak.Vault, otp_app: :my_app
+end
+
+MyApp.Vault.start_link()
+```
+
+## Introduction
+
+When dealing with PII or other sensitive data, we often want to encrypt this data, and control access to the decrypted values.
+
+To do this in `Ash`, we do that with `AshCloak`. See the getting started guide in `AshCloak` for installation instructions.
+
+## Encrypting attributes
+
+1. If you have not yet, follow the getting started guide for `AshCloak` and `Cloak`
+2. Add the `AshCloak` extension to your resource
+3. Configure the attributes that should be encrypted
+4. Add any other additional desired configuration (provided by `AshCloak`)
+
+## Examples
+
+
+
+```elixir
+defmodule User do
+ use Ash.Resource,
+ domain: Domain,
+ data_layer: Ash.DataLayer.Ets,
+ extensions: [AshCloak]
+
+ cloak do
+ vault MyApp.Vault
+ attributes [:ssn]
+ end
+
+ attributes do
+ uuid_primary_key :id
+ attribute :ssn, :string, allow_nil?: false
+ end
+
+ actions do
+ defaults [:read, create: [:ssn], update: [:ssn]]
+ end
+end
+
+defmodule Domain do
+ use Ash.Domain,
+ validate_config_inclusion?: false
+
+ resources do
+ resource User do
+ define(:create_user, action: :create, args: [:ssn])
+ define(:update_user, action: :update, args: [:ssn])
+ define(:list_users, action: :read)
+ end
+ end
+end
+```
+
+
+
+```
+{:module, Domain, <<70, 79, 82, 49, 0, 1, 255, ...>>,
+ [
+ Ash.Domain.Dsl.Resources.Resource,
+ Ash.Domain.Dsl.Resources.Options,
+ Ash.Domain.Dsl,
+ %{opts: [], entities: [...]},
+ Ash.Domain.Dsl,
+ Ash.Domain.Dsl.Resources.Options,
+ ...
+ ]}
+```
+
+## Data is encrypted when modified and is *not displayed* when inspecting.
+
+```elixir
+user = Domain.create_user!("111-11-1111")
+```
+
+
+
+```
+#User<
+ __meta__: #Ecto.Schema.Metadata<:loaded>,
+ id: "bc5284fe-294a-485e-8585-06130a4bca4e",
+ aggregates: %{},
+ calculations: %{},
+ ...
+>
+```
+
+```elixir
+# AshCloak turned ssn into a calculation
+user.ssn
+```
+
+
+
+```
+#Ash.NotLoaded<:calculation, field: :ssn>
+```
+
+```elixir
+# Load the value to decrypt it on-demand
+Ash.load!(user, :ssn).ssn
+```
+
+
+
+```
+"111-11-1111"
+```
diff --git a/documentation/how-to/preventing-concurrent-writes.livemd b/documentation/how-to/prevent-concurrent-writes.livemd
similarity index 100%
rename from documentation/how-to/preventing-concurrent-writes.livemd
rename to documentation/how-to/prevent-concurrent-writes.livemd
diff --git a/mix.exs b/mix.exs
index 40981238..e561ecec 100644
--- a/mix.exs
+++ b/mix.exs
@@ -79,7 +79,8 @@ defmodule Ash.MixProject do
"documentation/topics/security/policies.md",
"documentation/topics/reference/glossary.md",
"documentation/topics/reference/expressions.md",
- "documentation/how-to/preventing-concurrent-writes.livemd",
+ "documentation/how-to/encrypt-attributes.livemd",
+ "documentation/how-to/prevent-concurrent-writes.livemd",
"documentation/dsls/DSL:-Ash.Resource.md",
"documentation/dsls/DSL:-Ash.Domain.md",
"documentation/dsls/DSL:-Ash.Notifier.PubSub.md",
@@ -111,6 +112,7 @@ defmodule Ash.MixProject do
~r"documentation/dsls"
]
],
+ assets: ["documentation/assets"],
skip_undefined_reference_warnings_on: [
"CHANGELOG.md",
"documentation/topics/reference/glossary.md",
@@ -132,6 +134,7 @@ defmodule Ash.MixProject do
before_closing_head_tag: fn type ->
if type == :html do
"""
+