<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://www.yellowduck.be/pretty-atom-feed-v3.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <link href="https://www.yellowduck.be" rel="alternate"/>
  <link href="https://www.yellowduck.be/posts/feed" rel="self"/>
  <author>
    <name>Pieter Claerhout</name>
    <email>pieter@yellowduck.be</email>
  </author>
  <id>https://www.yellowduck.be/posts/feed</id>
  <title>🐥 YellowDuck.be</title>
  <updated>2026-08-07T08:00:00Z</updated>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/why-pogacar-rides-on-a-car-tire-brand" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Why are pro cyclists like Tadej Pogačar riding on tires made by car manufacturers? Jan Heine reveals the surprising truth behind tire sponsorship in the Tour de France. While companies like Continental dominate road racing sponsorships, it&apos;s primarily car tires being promoted, not bike tires. With costs running into millions, small brands struggle to compete despite offering quality products. Heine highlights that while small makers find success in gravel races, the high stakes of pro cycling often favor larger brands willing to invest heavily. The article provides an insightful look into the financial strategies behind sponsorships in elite road racing, illustrating how marketing priorities can overshadow the sport&apos;s rich cycling heritage.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.renehersecycles.com/why-pogacar-rides-on-a-car-tire-brand/&quot;&gt;Continue reading on &lt;strong&gt;www.renehersecycles.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sports&quot;&gt;#sports&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-07T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/why-pogacar-rides-on-a-car-tire-brand</id>
    <title>🔗 Why Pogačar rides on a car tire brand</title>
    <updated>2026-08-07T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/your-dropbox-is-now-a-skill-server" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;sx 2.0, a new app for Mac, Windows, and Linux, addresses the challenge of sharing AI skills among non-technical teams. By using a shared folder in Dropbox or similar services, teams can easily publish and synchronize their skills without needing git or terminal access. The app translates skills into various AI client formats seamlessly, allowing for effortless integration.&lt;/p&gt;
&lt;p&gt;Moreover, the release includes a pluggable extension system that enables teams to customize their workflows without bloating the core product. With built-in tools to monitor skill usage and ensure content quality, sx 2.0 targets marketing, legal, and sales teams who can now leverage AI without technical barriers through a simple, user-friendly interface. The emphasis on distribution aims to democratize access to AI knowledge across organizations.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://sleuth-io.github.io/sx/2026/07/10/your-dropbox-is-now-a-skill-server.html&quot;&gt;Continue reading on &lt;strong&gt;sleuth-io.github.io&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/announcement&quot;&gt;#announcement&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-06T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/your-dropbox-is-now-a-skill-server</id>
    <title>🔗 Your Dropbox is now a skill server</title>
    <updated>2026-08-06T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/kubernetes-dashboard-to-headlamp-a-step-by-step-guide" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Kubernetes Dashboard and Headlamp serve different roles in a cluster. Headlamp connects via &lt;code&gt;kubeconfig&lt;/code&gt;, allowing for desktop or in-cluster usage, while the Kubernetes Dashboard is strictly in-cluster. This article breaks down the processes for migrating from Dashboard to Headlamp. It highlights key differences in user experience, installation methods, and permissions management. A pre-migration checklist ensures a smooth transition, emphasizing the importance of understanding RBAC rules. By suggesting rollout plans, it gives teams structure in their migration approach. Ultimately, Headlamp simplifies managing multiple clusters with its user-friendly interface. It caters to both individual and shared access needs. This guide demystifies what it takes to move from Dashboard to Headlamp, equipping users with the information they need for a successful shift.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://kubernetes.io/blog/2026/07/13/kubernetes-dashboard-to-headlamp/&quot;&gt;Continue reading on &lt;strong&gt;kubernetes.io&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/kubernetes&quot;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-06T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/kubernetes-dashboard-to-headlamp-a-step-by-step-guide</id>
    <title>🔗 Kubernetes Dashboard to Headlamp: A Step-by-Step Guide</title>
    <updated>2026-08-06T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/markdown-support-coming-in-netnewswire" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;NetNewsWire is about to embrace &lt;code&gt;source:markdown&lt;/code&gt; elements in its next release. This move makes it the first mainstream RSS client to fully support Markdown. The integration allows WordLand-written WordPress posts, which already feature the necessary RSS feeds, to be published and rendered in Markdown. Users can simply log into WordLand and start creating content that will automatically be displayed in NetNewsWire via this format. A GitHub thread has been initiated to address any questions regarding this capability.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://daveverse.org/2025/10/30/markdown-support-coming-in-netnewswire/&quot;&gt;Continue reading on &lt;strong&gt;daveverse.org&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/announcement&quot;&gt;#announcement&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-06T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/markdown-support-coming-in-netnewswire</id>
    <title>🔗 Markdown support coming in NetNewsWire</title>
    <updated>2026-08-06T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/introducing-mishka-chelekom-v0-0-9" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Mishka Chelekom v0.0.9 revolutionizes how AI agents interact with Phoenix UI. This release adds a built-in MCP server, enabling AI to utilize your components accurately. In addition, it boasts over 35 new headless components that are unstyled yet fully accessible, allowing complete design customization without sacrificing functionality. The new Kit allows users to reuse and restyle components easily without altering original code, preserving project integrity. With a growing community behind it and over 55,000 downloads, this version solidifies Mishka Chelekom&apos;s position as a powerful tool for developers looking to integrate AI effectively into their Phoenix applications.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://mishka.tools/blog/introducing-mishka-chelekom-v0.0.9-ai-native-phoenix-components-with-mcp-headless-ui-and-the-kit&quot;&gt;Continue reading on &lt;strong&gt;mishka.tools&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/phoenix&quot;&gt;#phoenix&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-05T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/introducing-mishka-chelekom-v0-0-9</id>
    <title>🔗 Introducing Mishka Chelekom v0.0.9</title>
    <updated>2026-08-05T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/kubernetes-architecture" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Kubernetes isn’t just another container orchestration tool; it’s an essential piece of cloud-native architecture that transformed how applications are managed. Neo Kim and Ayaan explain Kubernetes architecture, breaking down its components like the control plane and worker nodes. They illustrate how Kubernetes operates as a distributed system, ensuring applications stay available and resilient even as demands change. The authors emphasize the importance of concepts like desired state versus actual state, highlighting Kubernetes’ ability to self-heal and maintain the system’s equilibrium. This article not only provides a solid foundation for understanding how Kubernetes works but also makes a compelling case for why mastering its architecture is crucial for modern developers and architects.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://newsletter.systemdesign.one/p/kubernetes-architecture&quot;&gt;Continue reading on &lt;strong&gt;newsletter.systemdesign.one&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/devops&quot;&gt;#devops&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/kubernetes&quot;&gt;#kubernetes&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-05T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/kubernetes-architecture</id>
    <title>🔗 Kubernetes architecture</title>
    <updated>2026-08-05T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/best-and-worst-software-projects-what-developers-really-think" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;AI significantly accelerates software delivery, but human decision-making remains crucial. Engineers recently discussed the role of AI in their projects, emphasizing its ability to enhance speed without replacing critical thinking and strategic choices. The insights reflect a balanced perspective on technology&apos;s place in development processes, highlighting the importance of creativity and judgment. In a rapidly evolving tech landscape, understanding the boundaries of AI&apos;s capabilities is essential for developers who aim to leverage it effectively while retaining control over their projects.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://shiftmag.dev/developers-are-the-reason-behind-best-and-worst-parts-of-software-development-10670/&quot;&gt;Continue reading on &lt;strong&gt;shiftmag.dev&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-05T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/best-and-worst-software-projects-what-developers-really-think</id>
    <title>🔗 Best and worst software projects: What developers really think</title>
    <updated>2026-08-05T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/prefer-strict-tables-in-sqlite" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Strict tables in SQLite avoid datatype issues by enforcing rigid typing, preventing common mistakes like inserting text into integer columns. By appending &lt;code&gt;STRICT&lt;/code&gt; to a table’s definition, users ensure that type mismatches are not allowed during inserts and updates. In contrast to non-strict tables, which permit any type in any column, strict tables help maintain data integrity and clarity. This article highlights the advantages and limitations of using strict tables, including better validation and avoiding creating columns with invalid types. While strict tables might introduce some performance considerations, the author argues that the benefits in data integrity far outweigh the potential drawbacks. The preference for strict tables is supported by examples and a discussion on SQLite&apos;s flexible typing.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://evanhahn.com/prefer-strict-tables-in-sqlite/&quot;&gt;Continue reading on &lt;strong&gt;evanhahn.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sqlite&quot;&gt;#sqlite&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-04T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/prefer-strict-tables-in-sqlite</id>
    <title>🔗 Prefer strict tables in SQLite</title>
    <updated>2026-08-04T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/rewriting-bun-in-rust" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Bun&apos;s evolution from Zig to Rust was not just a coding challenge; it aimed to resolve fundamental stability issues. The article details the ambitious journey of rewriting Bun&apos;s large codebase to enhance memory safety and eliminate bugs inherent in manual memory management. Originally a port from Go to Zig, the developer recognized weaknesses in Zig&apos;s handling of garbage-collected and manually-managed memory. By leveraging a new model from Anthropic, the team performed an efficient mechanical port to Rust, thus enabling safer code practices through Rust&apos;s strong type system and automatic memory management. The result? A more robust Bun, aiming for long-term stability and performance enhancement without sacrificing its original feature set. It&apos;s a case study in planning, execution, and leveraging AI in software development for increased reliability.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://bun.com/blog/bun-in-rust&quot;&gt;Continue reading on &lt;strong&gt;bun.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/rust&quot;&gt;#rust&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-04T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/rewriting-bun-in-rust</id>
    <title>🔗 Rewriting Bun in Rust</title>
    <updated>2026-08-04T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/ever-run-into-a-postgresql-query-that-you-can-figure-out-what-it-does" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Ever encountered an indecipherable PostgreSQL query that you still need to support? This article outlines how DBeaver&apos;s AI Assistant can demystify complex queries for users. With DBeaver 26.1.2, the AI Assistant analyzes queries and provides understandable explanations. The author illustrates this tool&apos;s efficacy using a sample query that identifies the top 10 most popular films by rental count and total revenue. From common table expressions (CTEs) to subqueries, the breakdown makes clear how the query&apos;s elements interact to produce results, highlighting the AI&apos;s utility when faced with obfuscated SQL. This guide empowers developers and database administrators to tackle intimidating SQL queries with confidence, showcasing an essential feature for anyone working with PostgreSQL.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://stokerpostgresql.blogspot.com/2026/07/ever-run-into-postgresql-query-that-you.html&quot;&gt;Continue reading on &lt;strong&gt;stokerpostgresql.blogspot.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/postgresql&quot;&gt;#postgresql&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sql&quot;&gt;#sql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-04T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/ever-run-into-a-postgresql-query-that-you-can-figure-out-what-it-does</id>
    <title>🔗 Ever run into a PostgreSQL query that you can figure out what it does??</title>
    <updated>2026-08-04T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/adding-2fa-to-phoenix-liveview-with-phx-gen-auth" rel="alternate"/>
    <content type="html">&lt;p&gt;Two-factor authentication (2FA) is a great way to improve account security. This post walks through how to add TOTP-based 2FA to a Phoenix LiveView app using the built-in &lt;code&gt;phx.gen.auth&lt;/code&gt; authentication system.&lt;/p&gt;
&lt;p&gt;We’ll cover:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding required fields and dependencies&lt;/li&gt;
&lt;li&gt;2FA setup flow using LiveView&lt;/li&gt;
&lt;li&gt;TOTP challenge during login&lt;/li&gt;
&lt;li&gt;LiveView-specific login handling&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Add dependencies&lt;/h2&gt;
&lt;p&gt;Add the following libraries to &lt;code&gt;mix.exs&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;deps&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:nimble_totp&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;~&gt; 1.0&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:qr_code&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;~&gt; 2.2&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then run:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-bash&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;mix&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;deps.get&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Extend the user schema&lt;/h2&gt;
&lt;p&gt;Add two fields to your &lt;code&gt;users&lt;/code&gt; table:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-comment&quot;&gt;# priv/repo/migrations/*_add_totp_to_users.exs&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:users&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-function-call&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:totp_secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:string&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-function-call&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:totp_confirmed_at&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:utc_datetime&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Migrate:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-bash&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;mix&lt;/span&gt; &lt;span class=&quot;l-variable-parameter&quot;&gt;ecto.migrate&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In your user schema:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-function-call&quot;&gt;schema&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;users&quot;&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-function-call&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:totp_secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:string&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-function-call&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:totp_confirmed_at&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:utc_datetime&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-comment&quot;&gt;# ...&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensure your &lt;code&gt;changeset/2&lt;/code&gt; casts these fields:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:totp_secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:totp_confirmed_at&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;validate_required&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Add 2FA setup LiveView&lt;/h2&gt;
&lt;p&gt;Create a new LiveView at &lt;code&gt;/settings/two_factor&lt;/code&gt; that generates a secret, renders a QR code, and lets the user enter their TOTP code.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyAppWeb.TwoFactorSetupLive&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyAppWeb&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:live_view&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;NimbleTOTP&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;QRCode&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Accounts&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt;_params&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_session&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;current_user&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;totp_secret&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;setup?: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;else&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;      &lt;span class=&quot;l-variable&quot;&gt;secret&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;encode32&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;NimbleTOTP&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;      &lt;span class=&quot;l-variable&quot;&gt;uri&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;NimbleTOTP&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;otpauth_uri&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;MyApp:&lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;decode32!&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;issuer: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;MyApp&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;png&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;QRCode&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:png&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;      &lt;span class=&quot;l-variable&quot;&gt;b64&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;encode64&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;png&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;       &lt;span class=&quot;l-function-call&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;         &lt;span class=&quot;l-string-special-symbol&quot;&gt;setup?: &lt;/span&gt;&lt;span class=&quot;l-boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;         &lt;span class=&quot;l-string-special-symbol&quot;&gt;secret: &lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;         &lt;span class=&quot;l-string-special-symbol&quot;&gt;qr_code: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;data:image/png;base64,&lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;b64&lt;/span&gt;&lt;span class=&quot;l-string-special&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;         &lt;span class=&quot;l-string-special-symbol&quot;&gt;code: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;24&quot;&gt;         &lt;span class=&quot;l-string-special-symbol&quot;&gt;error: &lt;/span&gt;&lt;span class=&quot;l-constant-builtin&quot;&gt;nil&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;25&quot;&gt;       &lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;26&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;27&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;28&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;29&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;handle_event&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;verify&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;code&quot;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;30&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;secret&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;decode32!&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;31&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;32&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;NimbleTOTP&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;valid?&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;33&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;34&quot;&gt;        &lt;span class=&quot;l-module&quot;&gt;Accounts&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;update_user&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;current_user&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;35&quot;&gt;          &lt;span class=&quot;l-string-special-symbol&quot;&gt;totp_secret: &lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;36&quot;&gt;          &lt;span class=&quot;l-string-special-symbol&quot;&gt;totp_confirmed_at: &lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;utc_now&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;37&quot;&gt;        &lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;38&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;39&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;40&quot;&gt;       &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;41&quot;&gt;       &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;put_flash&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:info&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;2FA enabled.&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;42&quot;&gt;       &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;push_redirect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;/settings&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;43&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;else&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;44&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;error: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Invalid code&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;45&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;46&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;47&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-heex&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;!--&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt; templates/two_factor_setup_live.html.heex &lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;--&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;Two-Factor Authentication&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;%=&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;setup?&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;l-tag-delimiter&quot;&gt;%&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;Scan this QR code in your authenticator app:&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;qr_code&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;l-tag-delimiter&quot;&gt;/&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;phx-submit&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;    &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;Code:&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;    &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;l-tag-delimiter&quot;&gt;/&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;    &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;%=&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;do: &lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;content_tag&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:p&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;class: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;text-red-500&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt; %&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;%&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt; %&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;2FA is already enabled.&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;%&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt; %&gt;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Add a TOTP challenge LiveView&lt;/h2&gt;
&lt;p&gt;When a user logs in with 2FA enabled, redirect them to a LiveView at &lt;code&gt;/two_factor&lt;/code&gt; to enter their code.&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyAppWeb.TwoFactorChallengeLive&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyAppWeb&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;:live_view&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Accounts&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;NimbleTOTP&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyAppWeb.UserAuth&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt;_params&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;pending_user_id&quot;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;    &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;user: &lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;Accounts&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;get_user!&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;code: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;error: &lt;/span&gt;&lt;span class=&quot;l-constant-builtin&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;10&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;11&quot;&gt;  &lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;handle_event&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;verify&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;code&quot;&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;12&quot;&gt;    &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;13&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;14&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;NimbleTOTP&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;valid?&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-module&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;decode32!&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;totp_secret&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;15&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;16&quot;&gt;       &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;17&quot;&gt;       &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;put_flash&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:info&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string&quot;&gt;&quot;Logged in with 2FA.&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;18&quot;&gt;       &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;UserAuth&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;log_in_user&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;19&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;else&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;20&quot;&gt;      &lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-function-call&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;error: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;Invalid code&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;21&quot;&gt;    &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;22&quot;&gt;  &lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;23&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-heex&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;!--&lt;/span&gt;&lt;span class=&quot;l-comment&quot;&gt; templates/two_factor_challenge_live.html.heex &lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;--&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;Enter your 2FA code&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;phx-submit&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;l-tag-attribute&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;l-tag-delimiter&quot;&gt;/&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;l-text&quot;&gt;Verify&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;/&lt;/span&gt;&lt;span class=&quot;l-tag&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&gt;&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;8&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;9&quot;&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt;&lt;%=&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;do: &lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;content_tag&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:p&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-constant&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;l-constant&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-string-special-symbol&quot;&gt;class: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;text-red-500&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;l-tag-delimiter&quot;&gt; %&gt;&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Update login logic&lt;/h2&gt;
&lt;p&gt;After validating the user’s password (in your LiveView or controller), check if TOTP is enabled:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;totp_confirmed_at&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Phoenix.LiveView&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;put_session&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:pending_user_id&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Phoenix.LiveView&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;/two_factor&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;else&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-module&quot;&gt;UserAuth&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;log_in_user&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In controllers, you’d use &lt;code&gt;put_session(conn, ...)&lt;/code&gt; and &lt;code&gt;redirect(conn, ...)&lt;/code&gt; instead.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Update &lt;code&gt;UserAuth&lt;/code&gt; helper&lt;/h2&gt;
&lt;p&gt;Make sure your &lt;code&gt;UserAuth&lt;/code&gt; module has a LiveView-compatible &lt;code&gt;log_in_user/3&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;lumis&quot;&gt;&lt;code class=&quot;language-elixir&quot; translate=&quot;no&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;1&quot;&gt;&lt;span class=&quot;l-keyword-function&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;l-function&quot;&gt;log_in_user&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-comment&quot;&gt;_params&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;\\&lt;/span&gt; &lt;span class=&quot;l-punctuation-special&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;l-keyword&quot;&gt;do&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;2&quot;&gt;  &lt;span class=&quot;l-variable&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;l-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;MyApp.Accounts&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;generate_user_session_token&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-variable&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;3&quot;&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;4&quot;&gt;  &lt;span class=&quot;l-variable&quot;&gt;socket&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;5&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Phoenix.LiveView&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;put_session&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;:user_token&lt;/span&gt;&lt;span class=&quot;l-punctuation-delimiter&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;l-variable&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;6&quot;&gt;  &lt;span class=&quot;l-operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;l-module&quot;&gt;Phoenix.LiveView&lt;/span&gt;&lt;span class=&quot;l-operator&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;l-function-call&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;l-string-special-symbol&quot;&gt;to: &lt;/span&gt;&lt;span class=&quot;l-string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;l-punctuation-bracket&quot;&gt;)&lt;/span&gt;
&lt;/div&gt;&lt;div class=&quot;l-line&quot; data-line=&quot;7&quot;&gt;&lt;span class=&quot;l-keyword&quot;&gt;end&lt;/span&gt;
&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;Done&lt;/h2&gt;
&lt;p&gt;You now have a working TOTP 2FA flow in Phoenix LiveView:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users opt in to 2FA in their settings&lt;/li&gt;
&lt;li&gt;A QR code is shown and confirmed&lt;/li&gt;
&lt;li&gt;2FA is required on next login&lt;/li&gt;
&lt;li&gt;All handled with clean LiveView flows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From here, you can extend the system with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Disabling 2FA&lt;/li&gt;
&lt;li&gt;Backup codes&lt;/li&gt;
&lt;li&gt;Remembering trusted devices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let me know if you&apos;d like a follow-up on any of those features.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/elixir&quot;&gt;#elixir&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/phoenix&quot;&gt;#phoenix&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/auth&quot;&gt;#auth&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-03T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/adding-2fa-to-phoenix-liveview-with-phx-gen-auth</id>
    <title>🐥 Adding 2FA to Phoenix LiveView with phx.gen.auth</title>
    <updated>2026-08-03T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/more-shell-patterns-i-use-weekly" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;This article shares useful Bash patterns and techniques that boost productivity for everyday tasks. The author highlights various methods like using &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;xargs&lt;/code&gt; for find-and-replace operations, employing &lt;code&gt;while&lt;/code&gt; loops until success, and parallelizing commands with &lt;code&gt;wait&lt;/code&gt;. The discussion emphasizes the importance of clean output and error handling, using temporary directories for logs with &lt;code&gt;mktemp&lt;/code&gt;, and enhancing user legibility with color-coded output. The author also advocates for efficient file manipulation using shell constructs, recommending &lt;code&gt;awk&lt;/code&gt; for filtering data instead of &lt;code&gt;grep&lt;/code&gt;. These patterns, though simple, can significantly streamline workflows, especially for those working with commands regularly.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://will-keleher.com/posts/more-shell-patterns-i-use-weekly/&quot;&gt;Continue reading on &lt;strong&gt;will-keleher.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/terminal&quot;&gt;#terminal&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/linux&quot;&gt;#linux&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-03T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/more-shell-patterns-i-use-weekly</id>
    <title>🔗 More shell patterns I use weekly</title>
    <updated>2026-08-03T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/why-backlogs-are-harmful-why-they-never-shrink-and-what-to-do-instead" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Backlogs never shrink, and their existence often leads to unproductive practices. When teams rely on them, they merely accumulate tasks that distract from prioritizing what truly matters. The article points out that important tasks rarely enter the backlog as teams instinctively know what needs immediate attention. This misalignment causes waste and elongates cycle times, as tasks languish unattended. A well-managed product team can avoid the pitfalls of backlogs by employing a high-level roadmap and maintaining close communication with engineers. In practice, this means regularly revisiting goals, effectively filtering bugs, and keeping the focus on immediate tasks. The author argues that instead of allowing unnecessary tasks to clutter workflows, it’s essential to be ruthless in prioritization and decision-making to enhance productivity and strategic alignment.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.lucasfcosta.com/blog/backlogs-are-useless&quot;&gt;Continue reading on &lt;strong&gt;www.lucasfcosta.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-03T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/why-backlogs-are-harmful-why-they-never-shrink-and-what-to-do-instead</id>
    <title>🔗 Why backlogs are harmful, why they never shrink, and what to do instead</title>
    <updated>2026-08-03T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-laravel-scheduler-under-the-hood" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Laravel&apos;s Scheduler features an API that simplifies task scheduling, but its underlying mechanics are crucial to understand. When cron triggers &lt;code&gt;php artisan schedule:run&lt;/code&gt;, Laravel takes over to determine which events are due. This article dives deep into the Laravel Scheduler, explaining how it processes scheduled tasks, from command definitions to execution. It clarifies concepts like &lt;code&gt;onOneServer()&lt;/code&gt; and &lt;code&gt;withoutOverlapping()&lt;/code&gt;, which are essential for managing concurrent tasks. Readers learn how Laravel creates event objects from command definitions and how frequency methods adjust cron expressions. The article emphasizes the distinctions between the Scheduler and the Queue system, underpinning the operational design decisions that make task scheduling effective in production environments. Armed with this insight, Laravel developers can better leverage the Scheduler for reliable task execution.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://wendelladriel.com/blog/the-laravel-scheduler-under-the-hood&quot;&gt;Continue reading on &lt;strong&gt;wendelladriel.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/laravel&quot;&gt;#laravel&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-02T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-laravel-scheduler-under-the-hood</id>
    <title>🔗 The Laravel Scheduler Under the Hood</title>
    <updated>2026-08-02T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/waze-now-supports-electric-vehicles-in-belgium" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Finding a charging station for your electric vehicle won&apos;t be difficult anymore. Waze has added full support for electric charging points on its map in Belgium, enabling users to enter their car and plug type. This upgrade allows Waze to pinpoint all available charging points in your vicinity or along your route.&lt;/p&gt;
&lt;p&gt;Thanks to the dedicated volunteer map editors, reliable information on charging points is now accessible. The community has worked tirelessly to update the Waze map with this critical data, ensuring it remains current. To configure electric vehicle support, users can navigate to Settings -&gt; Vehicle Details -&gt; Electric vehicles and toggle the EV features on, allowing them to easily find the nearest charging station. Users can also report outdated charging points for the community editors to review and update accordingly.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.wazebelgium.be/waze-now-supports-electric-vehicles-in-belgium/&quot;&gt;Continue reading on &lt;strong&gt;www.wazebelgium.be&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/announcement&quot;&gt;#announcement&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-02T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/waze-now-supports-electric-vehicles-in-belgium</id>
    <title>🔗 Waze now supports electric vehicles in Belgium</title>
    <updated>2026-08-02T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/claude-code-sends-4-7x-more-tokens-than-opencode-before-reading-your-prompt" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Claude Code&apos;s appetite for tokens far exceeds that of OpenCode, as the article reveals startling figures from direct comparisons. In experiments run under identical conditions, Claude Code averaged around 33,000 tokens for a one-line reply, while OpenCode only consumed about 7,000. The data reported showcases how Claude Code’s approach introduces significant cache inefficiencies, requiring 54 times more tokens for some tasks. A primary concern raised is the impact on production environments, particularly when operating under regulations like the EU AI Act. The emphasis is on understanding the tokens spent is crucial for optimizing agentic AI systems, where every token represents context that can&apos;t be utilized for task completion. Additionally, analyzing not just input but also cache writes reveals the complexity and potential costs associated with these AI harnesses.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://systima.ai/blog/claude-code-vs-opencode-token-overhead&quot;&gt;Continue reading on &lt;strong&gt;systima.ai&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-02T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/claude-code-sends-4-7x-more-tokens-than-opencode-before-reading-your-prompt</id>
    <title>🔗 Claude Code sends 4.7x more tokens than OpenCode before reading your prompt</title>
    <updated>2026-08-02T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/software-has-changed" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A surprising truth reveals how quickly the software landscape has transformed. In a recent tweet, Jamon Holmgren notes that many experienced engineers feel shell-shocked by these rapid changes. Development cycles are faster, yet there&apos;s a concern about not building the right things. The industry has shifted from deep focus to a management of constant new experiments, allowing for greater code review and refinement.&lt;/p&gt;
&lt;p&gt;Building faster doesn’t mean building better, but it does free up developers to tackle technical debt proactively. Holmgren reflects on this evolution, pondering the implications of how code is created versus its final quality. With the ability to learn and adapt, many are navigating this new landscape while aiming to produce trusted, quality code.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.abeautifulsite.net/posts/software-has-changed/&quot;&gt;Continue reading on &lt;strong&gt;www.abeautifulsite.net&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-01T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/software-has-changed</id>
    <title>🔗 Software has changed</title>
    <updated>2026-08-01T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/zig-creator-calls-spade-a-spade-anthropic-blows-smoke" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Anthropic&apos;s ambitious narrative suggests software engineering will vanish, backed by a staggering $132 billion investment and a looming IPO. However, skepticism is essential; with no profitability in sight, decisions based on such hype could lead to misguided architecture and staffing choices driven by fear. Andrew Kelley, the creator of Zig, openly criticized the migration of the Bun project from Zig to Rust, framing it as a marketing ploy rather than a technical necessity. This situation raises questions not only about the software&apos;s future but also about the reliability of narratives shaped by significant financial backing. With Bun’s heavy reliance on AI and the subsequent shift, the community now grapples with the implications of these choices in a world where every programming language is scrutinized against performance and credibility measures. The drama unfolds not just in technical decisions, but in the larger discourse around automation and responsibility in software development.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://raymyers.org/post/zed-creator-calls-spade-a-spade/&quot;&gt;Continue reading on &lt;strong&gt;raymyers.org&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/rust&quot;&gt;#rust&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-01T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/zig-creator-calls-spade-a-spade-anthropic-blows-smoke</id>
    <title>🔗 Zig creator calls spade a spade, Anthropic blows smoke</title>
    <updated>2026-08-01T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/de-morgans-law" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Logical expressions can turn complex quickly, but understanding De Morgan’s Law can simplify them effectively. This principle, attributed to Augustus de Morgan in the 19th century, states that certain expressions using NOT, AND, and OR operations can be rewritten without changing their results. For instance, NOT(A) AND NOT(B) is equivalent to NOT(A OR B). Tracy Gilmore illustrates this concept with practical examples, including a detailed breakdown of truth tables and expressions. The article provides a clear guide on how to apply De Morgan’s Law to make code more readable and efficient. It&apos;s a valuable resource for programmers looking to improve their logical condition handling and streamline their code without the harsh symbols of Boolean algebra. Gilmore’s straightforward explanations make the law accessible for both seasoned developers and those newer to coding.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://dev.to/tracygjg/de-morgans-law-a-little-computer-science-for-the-inquisitive-developer-4m2k&quot;&gt;Continue reading on &lt;strong&gt;dev.to&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-08-01T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/de-morgans-law</id>
    <title>🔗 De Morgan’s Law</title>
    <updated>2026-08-01T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/the-order-of-data-defaults-performance-determinism-paging" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;What happens when an SQL query doesn&apos;t specify the &lt;code&gt;ORDER BY&lt;/code&gt; clause? This article breaks down the consequences of relying on the default sorting behavior across different databases such as Postgres, MySQL, and MongoDB. It illustrates with detailed SQL operations how Postgres defaults to insertion order, while MySQL may sort rows by primary key. It also points out the risks of assuming that the default order will remain consistent after updates or deletions. The author emphasizes that each database&apos;s handling of data ordering can affect performance and predictability, making it crucial for developers to be explicit in their ordering strategy.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://binaryigor.com/the-order-of-data.html&quot;&gt;Continue reading on &lt;strong&gt;binaryigor.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/database&quot;&gt;#database&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/sql&quot;&gt;#sql&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-07-31T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/the-order-of-data-defaults-performance-determinism-paging</id>
    <title>🔗 The Order of Data: defaults, performance, determinism &amp; paging</title>
    <updated>2026-07-31T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/small-programming-tricks" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Small nuggets of knowledge can significantly enhance engineering productivity. This article reveals useful programming tricks that don&apos;t require extensive prior knowledge. For instance, using &lt;code&gt;python3 -m http.server&lt;/code&gt; for quickly starting a simple server is highlighted. The author also suggests tools like &lt;code&gt;fzf&lt;/code&gt; for fuzzy searching terminal history, &lt;code&gt;atuin&lt;/code&gt; for a searchable SQLite database of shell history, and practical database commands such as &lt;code&gt;SELECT&lt;/code&gt; without a &lt;code&gt;FROM&lt;/code&gt;. Techniques in regular expressions, modern JavaScript capabilities, and efficient &lt;code&gt;git&lt;/code&gt; commands are discussed as well. These tricks emphasize that even minor insights can lead to substantial efficiency gains in programming tasks. Sharing these nuggets within teams can foster a culture of learning and continuous improvement. Inspired by Julia Evan&apos;s approach, the article emphasizes that small bits of knowledge are both powerful and enjoyable to learn.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://will-keleher.com/posts/small-programming-tricks-matter/&quot;&gt;Continue reading on &lt;strong&gt;will-keleher.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/python&quot;&gt;#python&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/terminal&quot;&gt;#terminal&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/git&quot;&gt;#git&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-07-31T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/small-programming-tricks</id>
    <title>🔗 Small programming tricks</title>
    <updated>2026-07-31T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/lucas-laws-of-project-management" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;A project’s success hinges on solving the actual problem, rather than solely adhering to a specification. Surprisingly, compliance with the initial scope is often misguided because the primary focus should be on user needs. As a project unfolds, the understanding of the problem evolves, necessitating adaptations in the solution. Prioritizing problem comprehension over rigid scope planning is key. As the article outlines, assumptions must be tested not for their ease but based on their potential cost if proven wrong. It emphasizes the importance of clear communication and shared understanding within the team, advocating for a streamlined backlog and adaptable plans. This method ensures that projects remain viable, meet deadlines, and continuously provide value throughout their lifecycle.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://lucasfcosta.com/blog/laws-of-software-project-management&quot;&gt;Continue reading on &lt;strong&gt;lucasfcosta.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-07-31T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/lucas-laws-of-project-management</id>
    <title>🔗 Lucas&apos; Laws of Project Management</title>
    <updated>2026-07-31T08:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/go-flavored-concurrency-in-c" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Go&apos;s concurrency model is attractive, but can it be mimicked in C using POSIX threads? This article delves into that question, exploring how to forge Go-like concurrency in C without runtime support and garbage collection. The writer introduces &lt;code&gt;Solod&lt;/code&gt;, a strict Go subset that compiles to C, utilizing POSIX thread constructs such as mutexes and condition variables as its foundation.&lt;/p&gt;
&lt;p&gt;The piece details various concurrency mechanisms like atomics, the worker pool, and task scheduling. Specific code snippets illustrate the direct translations from So to C, shedding light on performance trade-offs inherent to using OS threads versus lightweight goroutines. The core takeaway is clear: while achieving Go-like concurrency in C is challenging due to the shortcomings of POSIX threads, significant capabilities remain realizable by leveraging the right primitives and accepting the associated trade-offs.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://antonz.org/concurrency-in-c/&quot;&gt;Continue reading on &lt;strong&gt;antonz.org&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/golang&quot;&gt;#golang&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/pattern&quot;&gt;#pattern&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-07-30T17:00:00Z</published>
    <id>https://www.yellowduck.be/posts/go-flavored-concurrency-in-c</id>
    <title>🔗 Go-flavored concurrency in C</title>
    <updated>2026-07-30T17:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/my-ai-learning-journey" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Metrics have always been the so-called magic beans for improving quality in testing and quality engineering. In her article, Lisa Crispin emphasizes the importance of measuring AI success and quality improvement. She shares her decades of experience in the field and highlights how continuous quality improvement is crucial for modern software teams. The discussion revolves around effective metrics that can drive quality initiatives, making the case for an analytical approach to testing. Crispin brings a fresh perspective on harnessing metrics to elevate both AI performance and overall product quality.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://lisacrispin.com/2026/07/10/my-ai-learning-journey/&quot;&gt;Continue reading on &lt;strong&gt;lisacrispin.com&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/best-practice&quot;&gt;#best-practice&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/testing&quot;&gt;#testing&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-07-30T13:00:00Z</published>
    <id>https://www.yellowduck.be/posts/my-ai-learning-journey</id>
    <title>🔗 My AI learning journey</title>
    <updated>2026-07-30T13:00:00Z</updated>
  </entry>
  <entry>
    <author>
      <name>Pieter Claerhout</name>
      <email>pieter@yellowduck.be</email>
    </author>
    <link href="https://www.yellowduck.be/posts/good-tools-are-delightful" rel="alternate"/>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Good tools are designed with the user&apos;s needs in mind, leading to unexpected delight and increased productivity. Zach Daniel emphasizes how tools like Neovim create opportunities for learning and improvement, rather than simply fading into the background. He reflects on his own experience with plugins such as Neogit, which enhanced his understanding and efficiency in managing code. Daniel critiques the idea that effective tools should be invisible, arguing instead for tools that challenge users to grow. He also touches on the evolving landscape of tools with the rise of LLMs, noting their potential to streamline workflows without sacrificing depth. Ultimately, he champions the view that while good tools may disappear, the best ones empower and delight users.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;&lt;a href=&quot;https://www.zachdaniel.dev/p/good-tools-are-delightful&quot;&gt;Continue reading on &lt;strong&gt;www.zachdaniel.dev&lt;/strong&gt;&lt;/a&gt;&lt;p&gt;&lt;a href=&quot;https://www.yellowduck.be/tags/reading-list&quot;&gt;#reading-list&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/development&quot;&gt;#development&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/tools&quot;&gt;#tools&lt;/a&gt; &lt;a href=&quot;https://www.yellowduck.be/tags/ai&quot;&gt;#ai&lt;/a&gt;&lt;/p&gt;</content>
    <published>2026-07-30T08:00:00Z</published>
    <id>https://www.yellowduck.be/posts/good-tools-are-delightful</id>
    <title>🔗 Good tools are delightful</title>
    <updated>2026-07-30T08:00:00Z</updated>
  </entry>
</feed>