<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Literate Labs]]></title><description><![CDATA[Building web applications with Elixir]]></description><link>https://www.literatelabs.com</link><image><url>https://substackcdn.com/image/fetch/$s_!pGCI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd15326f3-6bb1-403b-91aa-d7eea10bb850_200x200.png</url><title>Literate Labs</title><link>https://www.literatelabs.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 06 May 2026 09:38:52 GMT</lastBuildDate><atom:link href="https://www.literatelabs.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Jon Crowell]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[literatelabs@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[literatelabs@substack.com]]></itunes:email><itunes:name><![CDATA[Jon Crowell]]></itunes:name></itunes:owner><itunes:author><![CDATA[Jon Crowell]]></itunes:author><googleplay:owner><![CDATA[literatelabs@substack.com]]></googleplay:owner><googleplay:email><![CDATA[literatelabs@substack.com]]></googleplay:email><googleplay:author><![CDATA[Jon Crowell]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[How to get verification emails for user registration working in a Phoenix 1.6 web application, using SendGrid]]></title><description><![CDATA[A step-by-step walkthrough]]></description><link>https://www.literatelabs.com/p/how-to-get-verification-emails-for</link><guid isPermaLink="false">https://www.literatelabs.com/p/how-to-get-verification-emails-for</guid><dc:creator><![CDATA[Jon Crowell]]></dc:creator><pubDate>Wed, 15 Sep 2021 17:39:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!zeWA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post I will walk through the steps required to create a web application written in <a href="https://en.wikipedia.org/wiki/Elixir_(programming_language)">Elixir</a>.  This web application, named &#8220;Socrates&#8221; since it is a teacher, will make use of the Phoenix 1.6 web application framework and will have working user registration with email verification.</p><p>In the next post I will walk through deploying this application on Fly.io, which is a cloud platform well-suited to Phoenix apps.</p><h2>Set up your development environment</h2><p>To begin, get yourself a MacBook.  I have not used a Windows machine in years and most web developers work on MacBooks. In addition, I will assume you have some familiarity with the command line and also with Git.</p><p>Now, originally I was going to merely instruct you to install Erlang and Elixir using the Homebrew package manager, which is the approach officially recommended by the instructions here: <a href="https://elixir-lang.org/install.html">https://elixir-lang.org/install.html</a>.</p><p>However, experience has taught me that over time a version manager will prove invaluable.  Suppose that you are reading this blog post a couple years from now and various updates and deprecations have proliferated throughout the ecosystem.  In this case you will certainly find that some incompatibility will prevent the project from working as expected.  In fact, I find that this is the case even now as I try to run examples that were created only a few months ago.  So it is important to bolt down the exact version of the underlying tools that will be used to create the project.</p><h4>Installing the asdf version manager</h4><p>In the past, version managers were created for different individual packages.  So, for instance, if you were a python developer then you would make use of <code>pyenv</code> for python version management, and if you were a Ruby developer then you would use <code>rbenv</code>, and there was also <code>NVM</code>, the node version manager, and so on.</p><p>Now it turns out that a single developer usually works with several different programming languages, and also that a single project makes use of a handful of different tools that each require versioning.  So rather than worrying about a different version manager for each of the dozen different tools that may be used on your development system, some smart person decided to create one version manager to rule them all.  This is the <a href="https://asdf-vm.com"><code>asdf</code> version manager</a>.  You can click that link to read a bit about it and to get the official installation instructions.</p><p>Once you have installed <code>asdf</code> you will have a <code>.asdf/</code> directory in your home directory, which is where the action will take place.  You can also type</p><pre><code>asdf plugin list all</code></pre><p>at your terminal command line to see all the different tools that <code>asdf</code> can be used to control the versions of.  You will notice that Elixir and Erlang are included in the list.</p><p>You should also see a <strong><code>.tool-versions</code></strong> file listed in your home directory, with a few tools + versions listed in it.  These are the global default versions.</p><h4>Install Elixir and Erlang</h4><p>Your first order of business, for the purposes of this project, is to install the latest versions of Erlang and Elixir (as of September, 2021), which are Erlang 24.0.6 and Elixir 1.12.3-otp-24.</p><p>Now that you have Elixir installed, you have access to <a href="https://hexdocs.pm/mix/Mix.html">a tool called <code>mix</code></a>, which &#8220;is a build tool that provides tasks for creating, compiling, and testing Elixir projects, managing its dependencies, and more.&#8221;</p><h4>Install Phoenix 1.6</h4><p>With <code>asdf</code> controlling versions, Elixir (and Erlang) installed, and <code>mix</code> available, we can now move on to installing the version of <a href="https://hexdocs.pm/phoenix/1.6.0-rc.0/Phoenix.html">Phoenix</a> that we will to use to build our web application, which is 1.6-rc.0.  &#8220;rc.0&#8221; means that this is the initial version of a &#8220;release candidate&#8221; for version 1.6.  A few weeks from now you may be able to just install version 1.6, but for now we are working with a preliminary version of 1.6.  To install Phoenix, run</p><pre><code>mix archive.install hex phx_new 1.6.0-rc.0</code></pre><p>If you&#8217;re curious where Phoenix 1.6-rc.0 was installed, it should be in <code>~/.asdf/installs/elixir/1.12.3-otp-24/.mix/archives</code></p><h4>Install PostgreSQL</h4><p>The final step we must take to get our development environment ready for our web application is to install PostgreSQL.  We are going to be building a database-backed application, and it doesn&#8217;t make sense to use any database other than PostgreSQL.  We need a local version for development and testing, and we&#8217;ll also set up a PostgreSQL database on Fly.io for our production deployment.</p><p>To install PostgreSQL you can either use Homebrew or one of the approaches suggested here: <a href="https://www.postgresql.org/download/macosx/">https://www.postgresql.org/download/macosx/</a>.  It is technically possible to use <code>asdf</code> to manage different versions of PostgreSQL, but this is probably not necessary.</p><h2>Generate a new Phoenix 1.6 project</h2><p>Now that our development environment is ready, we can generate a new Phoenix 1.6 project (<a href="https://hexdocs.pm/phoenix/1.6.0-rc.0/up_and_running.html">officially documented here</a>).  At your terminal command prompt type:</p><pre><code>mix phx.new socrates</code></pre><p>You should choose to fetch and install the dependencies when prompted.  This will create a directory called <code>socrates/</code> in which the project will reside. Then <code>cd</code> into your new project directory and run</p><pre><code>mix ecto.create</code></pre><p>in order to get your database set up.  Ecto is the database wrapper and query generator for Elixir. You should now be able to run</p><pre><code><code>mix test</code></code></pre><p>and see that some basic tests are working correctly for your applicaiton and then run</p><pre><code><code>mix phx.server</code></code></pre><p>which will start a local development web server so that you can view your web application in your browser at <a href="http://localhost:4000"><code>http://localhost:4000</code></a>.  It should look like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zeWA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zeWA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 424w, https://substackcdn.com/image/fetch/$s_!zeWA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 848w, https://substackcdn.com/image/fetch/$s_!zeWA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 1272w, https://substackcdn.com/image/fetch/$s_!zeWA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zeWA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png" width="1456" height="1382" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/d75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1382,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1010343,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!zeWA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 424w, https://substackcdn.com/image/fetch/$s_!zeWA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 848w, https://substackcdn.com/image/fetch/$s_!zeWA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 1272w, https://substackcdn.com/image/fetch/$s_!zeWA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd75b1d81-f7a8-4652-9a1c-3fa955f143e4_1738x1650.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The last step we should take in this initial state is to add a <code>.tool-versions</code> file to our project so that we have the proper versions of Erlang and Elixir bolted down in case we want to revisit this project in a few months, after the ecosystem has moved on.  Inside your new <code>socrates/</code> directory create a file called <code>.tool-versions</code> with the following two lines:</p><pre><code>erlang 24.0.6
elixir 1.12.3-otp-24</code></pre><p>(Of course you should be sure that these are in fact the versions of Erlang and Elixir that you used to generate this project.) In the future, when someone clones your repository they will be able to run the command</p><pre><code>asdf install</code></pre><p>from the project directory and <code>asdf</code> will install the versions listed in the <code>.tool-versions</code> file and make sure that they are used for the project.</p><p>Your project should now look like the <a href="https://github.com/LiterateLabs/Socrates/tree/c3706c92424a6ca0e3a7ef134477e6e061a4abab">first commit</a> in the example project on GitHub at: <a href="https://github.com/LiterateLabs/Socrates">https://github.com/LiterateLabs/Socrates</a>.  Incidentally, you can see a list of all the commits at <a href="https://github.com/LiterateLabs/Socrates/commits">https://github.com/LiterateLabs/Socrates/commits</a>, which you can use to follow along with the steps of this tutorial.</p><h2>Add user accounts</h2><p>The next step is to generate the files for user account creation.  Phoenix has included a <a href="https://hexdocs.pm/phoenix/1.6.0-rc.0/mix_phx_gen_auth.html">mix task that generates a basic user account system</a>. </p><pre><code>mix phx.gen.auth Accounts User users</code></pre><p>This files that this mix task generates have been carefully reviewed for security by the the elixir community and are based on the ideas sketched out in <a href="https://dashbit.co/blog/a-new-authentication-solution-for-phoenix">this blog post by Jose Valim</a>, the creator of Elixir.</p><p>This user authentication system will allow users to register, will send a confirmation email with a link they can click on to verify their email address, prevents multiple users from signing up with the same email address, ensures the passwords are long enough, encrypts the password properly, and allows users to change their email or their password.  It has all the basics down to get your application up and running with functional user accounts.</p><p>Once these files have been generated you will have to again run </p><pre><code>mix deps.get</code></pre><p>because your <code>mix.exs</code> file will have been modified to add <code>{:bcrypt_elixir, "~&gt; 2.0"}</code> and this dependency needs to be installed.  You will then need to run</p><pre><code>mix ecto.migrate</code></pre><p>because some database tables to store the user information have been specified and now need to be created.  Now that these new files have been generated you can again run</p><pre><code>mix phx.server</code></pre><p>and see the application running at <a href="http://localhost:4000">http://localhost:4000</a>, this time with a user registration system:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Wc3E!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Wc3E!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 424w, https://substackcdn.com/image/fetch/$s_!Wc3E!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 848w, https://substackcdn.com/image/fetch/$s_!Wc3E!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 1272w, https://substackcdn.com/image/fetch/$s_!Wc3E!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Wc3E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png" width="1456" height="1382" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/d3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1382,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:819859,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Wc3E!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 424w, https://substackcdn.com/image/fetch/$s_!Wc3E!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 848w, https://substackcdn.com/image/fetch/$s_!Wc3E!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 1272w, https://substackcdn.com/image/fetch/$s_!Wc3E!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3ba6243-752a-494a-873f-9efd0d7cf30a_1738x1650.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>These links are functional and you can play around with registering yourself as a user and then logging in.  Your project should now match the <a href="https://github.com/LiterateLabs/Socrates/tree/d7e2ccac267ee144f65128d9651a2c35b9f2f214">second commit</a> in the example project. Note that that the <code>deps/</code>, <code>_build/</code>, and <code>priv/static/assets/</code> directories are listed in the <code>.gitignore</code> file and so are excluded from version control and do not appear in the GitHub code repository.</p><h2>Getting email verification working</h2><p>As you play around with the user registration in your new web application may notice that you can register a user for your system with any email address and that if you use your own email address then you do not in fact receive an email with a confirmation link in it.  To understand what is happening here, let&#8217;s examine some of the files in the project.</p><p>The first thing to observe is that there is a file called <code>user_notifier.ex</code> in the <code>lib/socrates/accounts/</code> directory.  This file contains three public functions, <code>deliver_confirmation_instructions</code>, <code>deliver_reset_password_instructions</code>, and <code>deliver_update_email_instructions</code> each of which calls the single deliver/3 private function in the file, which is defined as follows:</p><pre><code>defp deliver(recipient, subject, body) do
  email =
    new()
    |&gt; to(recipient)
    |&gt; from({"MyApp", "contact@example.com"})
    |&gt; subject(subject)
    |&gt; text_body(body)

  with {:ok, _metadata} &lt;- Mailer.deliver(email) do
    {:ok, email}
  end
end</code></pre><p>Any time our application wants to send a user an email, it will go use this private <code>deliver/3</code> function, so we can add a print statement to this function to inspect the email it is trying to send in order to verify if it is being executed, as follows:</p><pre><code>defp deliver(recipient, subject, body) do
  email =
    new()
    |&gt; to(recipient)
    |&gt; from({"MyApp", "contact@example.com"})
    |&gt; subject(subject)
    |&gt; text_body(body)

  IO.inspect(email)

  with {:ok, _metadata} &lt;- Mailer.deliver(email) do
    {:ok, email}
  end
end</code></pre><p>We will then be able to see in the terminal output that the system is in fact printing the email that is being sent, so we know that this function is being called. The terminal output with the email being inspected should look something like this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!50_O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!50_O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 424w, https://substackcdn.com/image/fetch/$s_!50_O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 848w, https://substackcdn.com/image/fetch/$s_!50_O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!50_O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!50_O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png" width="1456" height="910" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/a92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:910,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2024752,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!50_O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 424w, https://substackcdn.com/image/fetch/$s_!50_O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 848w, https://substackcdn.com/image/fetch/$s_!50_O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 1272w, https://substackcdn.com/image/fetch/$s_!50_O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fa92b7584-4e7e-423b-a468-e6e66d47b01c_2880x1800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>We can see that some insert statements are being made to enter the newly registered user into our database and that the email we are trying to inspect indeed exists. We can even see that this is a Swoosh email, which makes sense since Swoosh is the package that Phoenix has included to handle emails.  (You can <a href="https://hexdocs.pm/swoosh/Swoosh.html">read about Swoosh at HexDocs</a>). So this means that the deliver function is being called and executed correctly.  So where is the email going?</p><p>The answer lies in <code>config/config.exs</code>.  There is a section there from lines 21 - 31 that discusses configuring the Swoosh email package:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PJqk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PJqk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 424w, https://substackcdn.com/image/fetch/$s_!PJqk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 848w, https://substackcdn.com/image/fetch/$s_!PJqk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 1272w, https://substackcdn.com/image/fetch/$s_!PJqk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PJqk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png" width="1456" height="491" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:491,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:414953,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PJqk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 424w, https://substackcdn.com/image/fetch/$s_!PJqk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 848w, https://substackcdn.com/image/fetch/$s_!PJqk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 1272w, https://substackcdn.com/image/fetch/$s_!PJqk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F77947d75-64bb-4aee-8c5f-6291d7fd08db_1774x598.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Swoosh comes with adapters for a variety of email services, such as SendGrid, Sendinblue, Sendmail, Mandrill, Mailgun, Mailjet, and several others &#8212; you can read about them in the <a href="https://hexdocs.pm/swoosh/Swoosh.html#module-adapters">Swoosh adapter docs</a>.  But as a default Phoenix has Swoosh configured to use the Local adapter, which is meant for development and is not intended to actually send real emails.  The emails are only going as far as our local system, and we can view them in our app by going to <a href="http://localhost:4000/dev/mailbox">http://localhost:4000/dev/mailbox</a>. </p><p>Incidentally, we can also glance in <code>lib/socrates_web/router.ex</code> at lines 47-57 to see that by default Phoenix has configured a route for us to view these local emails in development mode:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZvkR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZvkR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 424w, https://substackcdn.com/image/fetch/$s_!ZvkR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 848w, https://substackcdn.com/image/fetch/$s_!ZvkR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 1272w, https://substackcdn.com/image/fetch/$s_!ZvkR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZvkR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png" width="752" height="217" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/e884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:217,&quot;width&quot;:752,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:88840,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZvkR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 424w, https://substackcdn.com/image/fetch/$s_!ZvkR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 848w, https://substackcdn.com/image/fetch/$s_!ZvkR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 1272w, https://substackcdn.com/image/fetch/$s_!ZvkR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe884bf36-6fbd-4349-8d8b-215f92957ed4_752x217.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h4>Getting a SendGrid API key</h4><p>So in order to get real emails sent, we will need to sign up with a service that allows us to send emails.  I have chosen to go with SendGrid for the time being.  If you want to follow along, then <a href="https://sendgrid.com">go to SendGrid.com</a> and click the &#8220;Start for Free&#8221; button in the corner and then give them the information they request.  Once you have an account set up and you are at your dashboard, then you can generate an API key with which to send emails from a web application.  It is under &#8220;Settings&#8221; in the menu on the left.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QjU1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QjU1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 424w, https://substackcdn.com/image/fetch/$s_!QjU1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 848w, https://substackcdn.com/image/fetch/$s_!QjU1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 1272w, https://substackcdn.com/image/fetch/$s_!QjU1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QjU1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png" width="1456" height="1260" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1260,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1439994,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QjU1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 424w, https://substackcdn.com/image/fetch/$s_!QjU1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 848w, https://substackcdn.com/image/fetch/$s_!QjU1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 1272w, https://substackcdn.com/image/fetch/$s_!QjU1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44c47a32-4f62-4c60-9fe2-7180a3003bfc_2328x2014.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e2Ma!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e2Ma!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 424w, https://substackcdn.com/image/fetch/$s_!e2Ma!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 848w, https://substackcdn.com/image/fetch/$s_!e2Ma!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 1272w, https://substackcdn.com/image/fetch/$s_!e2Ma!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e2Ma!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png" width="1371" height="1193" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1193,&quot;width&quot;:1371,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:507969,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!e2Ma!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 424w, https://substackcdn.com/image/fetch/$s_!e2Ma!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 848w, https://substackcdn.com/image/fetch/$s_!e2Ma!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 1272w, https://substackcdn.com/image/fetch/$s_!e2Ma!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F96b8c32e-7408-45bc-b6d7-d5336021adc4_1371x1193.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LfgR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LfgR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 424w, https://substackcdn.com/image/fetch/$s_!LfgR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 848w, https://substackcdn.com/image/fetch/$s_!LfgR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 1272w, https://substackcdn.com/image/fetch/$s_!LfgR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LfgR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png" width="1371" height="1193" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1193,&quot;width&quot;:1371,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:265670,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!LfgR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 424w, https://substackcdn.com/image/fetch/$s_!LfgR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 848w, https://substackcdn.com/image/fetch/$s_!LfgR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 1272w, https://substackcdn.com/image/fetch/$s_!LfgR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F148ae3e8-f763-4625-ad97-97ec70aacb72_1371x1193.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jzHz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jzHz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 424w, https://substackcdn.com/image/fetch/$s_!jzHz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 848w, https://substackcdn.com/image/fetch/$s_!jzHz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 1272w, https://substackcdn.com/image/fetch/$s_!jzHz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jzHz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png" width="1371" height="1193" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1193,&quot;width&quot;:1371,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:488570,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jzHz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 424w, https://substackcdn.com/image/fetch/$s_!jzHz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 848w, https://substackcdn.com/image/fetch/$s_!jzHz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 1272w, https://substackcdn.com/image/fetch/$s_!jzHz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8c1e0630-0bca-443a-8be3-813ed61e1a6a_1371x1193.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Following the screen shots above, go ahead and hit the button to create an API key, give it a meaningful name, and grant it full access.  Once you&#8217;ve done all that, copy the API key and store it in a secure place.  This key should be kept secret because it grants access to your SendGrid account and a malicious person could do bad things with it.  So we don&#8217;t want to keep this API key in our project directory because we don&#8217;t want to inadvertently commit it to our git repository.  (It is a nuisance to remove information from Git because Git is so assiduous about tracking the history of a project).</p><p>Wherever the API key is permanently stored, the way to give our application access to it will be through setting it as an environment variable &#8212; just add the following line to your <code>.bashrc</code> or <code>.zshrc</code> file so that the <code>SENDGRID_API_KEY</code> environment variable is set whenever you start your terminal:</p><pre><code>export SENDGRID_API_KEY='SG.xxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxx'</code></pre><p>Now, from your terminal you should be able to enter the command</p><pre><code>echo $SENDGRID_API_KEY</code></pre><p>and see the API key appear.  We will now be able to load this API key into our application through our config system.</p><h4>Understanding the Phoenix <strong><code>config/</code></strong> directory</h4><p>The <a href="https://hexdocs.pm/phoenix/1.6.0-rc.0/directory_structure.html#content">phoenix directory structure docs</a> have the following to say about the <code>config/</code> directory:</p><blockquote><p>The <code>config/config.exs</code> file is the entry point for your configuration. At the end of the <code>config/config.exs</code>, it imports environment specific configuration, which can be found in <code>config/dev.exs</code>, <code>config/test.exs</code>, and <code>config/prod.exs</code>. Finally, <code>config/runtime.exs</code> is executed and it is the best place to read secrets and other dynamic configuration.</p></blockquote><p>You may notice that the last line of <code>config/config.exs</code> reads:</p><pre><code>import_config "#{config_env()}.exs"</code></pre><p>this is the line that will load the environment-specific configuration for your application, which will over-ride configuration settings that have been set higher up in your <code>config.exs</code> file.</p><p>The problem with loading our API key in either our <code>config.exs</code> file or one of the environment-specific config files is that these files are executed at compile time in order to generate the artifact that will reside in our <code>_build/</code> directory and possibly be deployed to our production environment.  This means that our secret key will actually be included in the compiled artifact and an attacker with access to the compiled program could reverse-engineer it to extract the key.</p><p>Ideally we want the secret key loaded into our program after it has been compiled but before it starts executing &#8212; i.e., at the moment that it starts up.  This also allows us to change our API key after we have deployed our application to production &#8212; rather than re-compiling and re-deploying our application if we change the API key we would merely need to restart it in order to read in the new key from the environment.</p><p>This is what the <code>config/runtime.exs</code> script does &#8212; it is executed after the program has been compiled but before the program starts.  If we load our API key from this script then the key will never be stored anywhere except in environment variables and in the RAM of the running program, thereby considerably raising the bar for the malicious attacker we are concerned about.</p><h4>Configuring Swoosh to actually send emails</h4><p>If you take a look at the <code>config/runtime.exs</code> file you can see that it in fact already has a commented-out section from lines 51 - 67 with instructions for configuring the Swoosh Mailer &#8212; the example they have refers to the Mailgun adapter, but we will use SendGrid instead.</p><p>Let&#8217;s edit <code>config/runtime.exs</code> to remove lines 51 - 67 and then add the following lines outside of the <code>if config_env() == :prod do</code> block:</p><pre><code>if config_env() == :prod or config_env() == :dev do
  # Configuring the mailer
  config :socrates, Socrates.Mailer,
    adapter: Swoosh.Adapters.Sendgrid,
    api_key: System.get_env("SENDGRID_API_KEY")
 
  config :swoosh, :api_client, Swoosh.ApiClient.Finch
end</code></pre><p>Note that we still want to constrain our email configuration to the <code>prod</code> and <code>dev</code> environments &#8212; we don&#8217;t want real emails going out when we run our tests.  It is also worth noting that we have to configure Swoosh with an api_client &#8212; I have chosen Finch after reading <a href="https://elixirforum.com/t/mint-vs-finch-vs-gun-vs-tesla-vs-httpoison-etc/38588/11">this discussion on the Elixir Forum about HTTP clients</a>. </p><p>In addition to specifying <code>Swoosh.ApiClient.Finch</code> in our <code>config/runtime.exs</code> file, we will also have to add Finch as a dependency in our <code>mix.exs</code> file and start it in our applications supervision tree.  This is explained in the <a href="https://hexdocs.pm/swoosh/Swoosh.ApiClient.Finch.html">Swoosh docs on using Finch</a>.</p><p>Once these adjustments have been accomplished the <code>start/2</code> function in <code>lib/socrates/application.ex</code> should look like this:</p><pre><code>  @impl true
  def start(_type, _args) do
    children = [
      # Start the Ecto repository
      Socrates.Repo,
      # Start the Telemetry supervisor
      SocratesWeb.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: Socrates.PubSub},
      # Start the Endpoint (http/https)
      SocratesWeb.Endpoint,
      {Finch, name: Swoosh.Finch}
      # Start a worker: Socrates.Worker.start_link(arg)
      # {Socrates.Worker, arg}
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Socrates.Supervisor]
    Supervisor.start_link(children, opts)
  end</code></pre><p>and</p><pre><code>{:finch, "~&gt; 0.8"},</code></pre><p>should be listed in the project dependencies section of <code>mix.exs</code>.</p><p>Even with the correct API key loaded and Finch set up correctly, we still have to take one more step to get our email system working correctly.  SendGrid turns out to be a responsible email service, so it will not allow us to send emails that are not properly associated with a domain or email address.  Since I will be deploying this app to Socrates.LiterateLabs.com, I have gone ahead and authenticated my LiterateLabs.com domain with SendGrid.  You can do the same for your own domain by following <a href="https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication">SendGrid&#8217;s domain-authentication instructions here</a>. This amounts to making a few changes to your DNS settings.</p><p>Once your domain is authenticated you can adjust the <code>lib/socrates/accounts/user_notifier.ex</code> file to send the email from your authenticated domain:</p><pre><code># Delivers the email using the application mailer.
defp deliver(recipient, subject, body) do
  email =
    new()
    |&gt; to(recipient)
    |&gt; from({"Socrates", "Socrates@LiterateLabs.com"})
    |&gt; subject(subject)
    |&gt; text_body(body)

  with {:ok, _metadata} &lt;- Mailer.deliver(email) do
    {:ok, email}
  end
end</code></pre><p>You will now have to run <code>mix deps.get</code> to install the new fitch dependency and restart the application at the command line.  Upon restarting you should start receiving emails from your application if you try and register a user with your email address.</p><p>Your project should now match the <a href="https://github.com/LiterateLabs/Socrates/tree/a0f2782d0a08473b365f7f9eae2f8ebe3f932fa0">third commit</a> in the example project on Github.</p><p>Now that emails are properly being sent we can go ahead and deploy this application on Fly.io, which is explained in the next post.</p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Literate Labs]]></title><description><![CDATA[Welcome to Literate Labs by me, Jon Crowell.]]></description><link>https://www.literatelabs.com/p/coming-soon</link><guid isPermaLink="false">https://www.literatelabs.com/p/coming-soon</guid><dc:creator><![CDATA[Jon Crowell]]></dc:creator><pubDate>Sun, 22 Nov 2020 22:37:35 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!pGCI!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fd15326f3-6bb1-403b-91aa-d7eea10bb850_200x200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to Literate Labs by me, Jon Crowell. Happy to be here.</p><p>Sign up now so you don&#8217;t miss the first issue.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.literatelabs.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.literatelabs.com/subscribe?"><span>Subscribe now</span></a></p><p>In the meantime, <a href="https://www.literatelabs.com/p/coming-soon?utm_source=substack&utm_medium=email&utm_content=share&action=share">tell your friends</a>!</p>]]></content:encoded></item></channel></rss>