<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<atom:link href="https://seanbehan.ca/rss.xml" rel="self" type="application/rss+xml" />
		<title>Sean Behan</title>
		<link>https://seanbehan.ca</link>
		<description>Sean Behan&apos;s website and blog</description>
		<language>en-CA</language>
		<!-- RSS wants an address here and readers show the name beside it. -->
		<managingEditor>sean@seanbehan.ca (Sean Behan)</managingEditor>
		<webMaster>sean@seanbehan.ca (Sean Behan)</webMaster>
		<lastBuildDate>Fri, 28 Aug 2026 09:50:58 GMT</lastBuildDate>
		<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/how-i-survived-a-ddos-flood</guid>
		<title><![CDATA[How I survived a DDoS Flood]]></title>
		<description><![CDATA[CloudFlare L3/L4 is amazing. Today I not only survived a DDoS attack. My site didn’t even go down. W Cloudflare!]]></description>
		<link>https://seanbehan.ca/posts/how-i-survived-a-ddos-flood</link>
		<pubDate>Fri, 28 Aug 2026 04:16:26 GMT</pubDate>
		<category>cloudflare</category><category>emdash</category><category>networking</category>
		<content:encoded><![CDATA[<p>CloudFlare L3/L4 is amazing. Today I not only survived a DDoS attack. My site didn’t even go down. W Cloudflare!</p><p>How you ask? By hosting the entire site on Cloudflare’s Edge network. It allows me to host a CMS (shoutout to <a href="https://github.com/emdash-cms/emdash">EmDash</a> by Cloudflare). While also being basically immune to DDoS.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/emdash-and-cloudflare-workers</guid>
		<title><![CDATA[EmDash and Cloudflare Workers]]></title>
		<description><![CDATA[A post explaining that this blog has migrated from SvelteKit to EmDash]]></description>
		<link>https://seanbehan.ca/posts/emdash-and-cloudflare-workers</link>
		<pubDate>Mon, 24 Aug 2026 23:53:36 GMT</pubDate>
		<category>cloudflare</category><category>emdash</category>
		<content:encoded><![CDATA[<p>Today I migrated from SvelteKit to EmDash.</p><p></p><p>EmDash is a CMS made by Cloudflare to replace WordPress. This will be my first post made using the platform.</p><p></p><p>It works by hosting posts on Cloudflare D1 and images on R2. So it requires an active billing account. It also requires Workers Paid due to the size constraints of Workers Free (free Workers can only be up to 3MB).</p><p></p><p>So far it’s working great. I can publish posts directly from the admin panel, and it’s all still cached on Edge.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/usb-cable-testing</guid>
		<title><![CDATA[USB Cable Testing on Linux]]></title>
		<description><![CDATA[usbmon turns Wireshark into a cable tester, and finds retransmissions on cables that supposedly work.]]></description>
		<link>https://seanbehan.ca/posts/usb-cable-testing</link>
		<pubDate>Wed, 15 Apr 2026 07:11:06 GMT</pubDate>
		<category>linux</category><category>test</category><category>usb</category><category>wireshark</category>
		<content:encoded><![CDATA[<p>I was interested in testing my USB cables that I know work, but wanted to see if they had any retransmission going on.</p><p>Then today, I found out Linux has a kernel module that allows you to &quot;monitor&quot; usb interfaces. Like... using wireshark.</p><p>I won&#x27;t explain how to set up wireshark for your distro, that&#x27;s for you to figure out.</p><p>Once you have wireshark set up, you just <code>sudo modprobe usbmon</code> then <code>lsusb</code>.</p><p>Find the USB interface you want to monitor in the list, then select it when starting wireshark (gui).</p><p>That&#x27;s about it... you can filter like this:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>usb.urb_status != 0 &#x26;&#x26; !(usb.urb_status == -115)</span></span></code></pre><p>Short post, but hope this helps you test your own USB cables for any errors that aren&#x27;t shown in dmesg, like failed packets.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/nixos-uki</guid>
		<title><![CDATA[Secure Boot NixOS UKI Installer]]></title>
		<description><![CDATA[A flake that builds a signed unified kernel image, for rescuing a Steam Deck locked out by Secure Boot.]]></description>
		<link>https://seanbehan.ca/posts/nixos-uki</link>
		<pubDate>Sun, 06 Jul 2025 17:57:04 GMT</pubDate>
		<category>nixos</category><category>secure-boot</category><category>steam-deck</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>In this post I simply want to share a NixOS flake that might help you in the</p><p>case you enabled secure boot on a device such as the Steam Deck OLED, still</p><p>have keys because you backed them up, and want to get back in to either disable</p><p>secure boot with sbctl, or fix your unbootable NixOS system.</p><h3>Configuration</h3><p>First, here is the flake that will load your configuration:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ""</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  inputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    nixpkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "github:NixOS/nixpkgs/nixos-unstable"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  outputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nixpkgs }:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "x86_64-linux"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> import</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">inherit</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      nixosImage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">nixosSystem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        inherit</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        modules</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">          ./image-config.nix</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    in</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      packages</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.${</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">disk-image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixosImage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">build</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"image"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      defaultPackage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.${</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">} </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">packages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">${</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">disk-image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><p>Now the config that you can use to build an image.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    modulesPath</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    imports</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">modulesPath</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/image/repart.nix"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">loader</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">grub</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">enable</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">initrd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">availableKernelModules</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"usb_storage"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supportedFilesystems</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"btrfs"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    hardware</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">enableAllHardware</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">systemPackages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> with</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; [</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">      btrfs-progs</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # sbctl # if you want it to be able to disable secure boot</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">     ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    fileSystems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">device</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/dev/disk/by-label/nixos"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    networking</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      networkmanager</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        enable</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        wifi</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">backend</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "iwd"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      wireless</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">iwd</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        enable</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        codebam</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          isNormalUser</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          home</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/home/codebam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "Sean Behan"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          extraGroups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "wheel"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "networkmanager"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "libvirtd"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "video"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "uinput"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "wireshark"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "pipewire"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          hashedPassword</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "$6$TIP8YR83obmkq8T2$T3lYdPbPj9wysMznNlS5J0qHo2eyTr43aF/ZWSMWHdNRob4dkBB0s3KpBLUgYRTyPZxbb1ZgeqCrrx.DEEkQX1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">repart</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "image"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      partitions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "esp"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          contents</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "/EFI/BOOT/BOOT</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">toUpper</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">stdenv</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hostPlatform</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">efiArch</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.EFI"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">source</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">              "</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">systemd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/lib/systemd/boot/efi/systemd-boot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">stdenv</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hostPlatform</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">efiArch</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.efi"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "/EFI/Linux/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">boot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">loader</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">ukiFile</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">source</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">              "</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">build</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">uki</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">${</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">boot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">loader</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">ukiFile</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          };</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          repartConfig</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "esp"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Format</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "vfat"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            SizeMinBytes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "96M"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        };</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "root"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          storePaths</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">build</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">toplevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          repartConfig</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "root"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Format</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "ext4"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Label</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "nixos"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Minimize</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "guess"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre><h3>Building</h3><h3>Building the Image</h3><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nix</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build</span></span></code></pre><p>Make sure you change the user and password, or you won&#x27;t be able to login. Also</p><p>note that if you are using a Steam Deck OLED like me, WiFi won&#x27;t work so you&#x27;ll</p><p>need to use either ethernet or an external wireless adapter that has support in</p><p>the kernel.</p><h3>Signing for Secure Boot</h3><p>So ok you burn it to your USB drive:</p><p>(replace X with your USB drive letter, and add sudo or elevate)</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> if=./result/image.raw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> of=/dev/sdX</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bs=4M</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> status=progress</span></span></code></pre><p>Now you have to sign it if you want secure boot to work. Assuming you already</p><p>have keys set up you just run:</p><p>(again with sudo or elevate)</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mount</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sdX1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sbctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sign</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt/EFI/BOOT/BOOTX64.EFI</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sbctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sign</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt/EFI/Linux/nixos.efi</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sync</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">umount</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre><h3>Booting and Repair</h3><p>Now you&#x27;re ready to boot it, so plug it into your USB hub or however you planned</p><p>on plugging it into the Steam Deck or your device.</p><p>Once you&#x27;re in you will have access to a NixOS installer, and you should know</p><p>what to do next to repair.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/quadlets</guid>
		<title><![CDATA[Podman Quadlets]]></title>
		<description><![CDATA[Quadlets describe a container as a systemd unit, so systemd owns the lifecycle instead of Podman.]]></description>
		<link>https://seanbehan.ca/posts/quadlets</link>
		<pubDate>Fri, 28 Mar 2025 14:39:58 GMT</pubDate>
		<category>containers</category><category>linux</category><category>podman</category><category>quadlet</category><category>systemd</category>
		<content:encoded><![CDATA[<h3>What are Quadlets?</h3><p>In this post I&#x27;ll show you what a quadlet is and how you can use it to manage</p><p>your containers.</p><p>Quadlets are a new way to manage containers using systemd. They are a new</p><p>format for systemd units that allow you to manage containers using systemd.</p><h3>Container Quadlets</h3><p>Here&#x27;s an example of a container quadlet.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/library/alpine:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sleep infinity</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span></code></pre><p>If you put this in ~/.config/containers/systemd/alpine.container you can start</p><p>it with <code>systemctl --user start alpine</code> and it will start a new container.</p><p>Just make sure to <code>systemctl --user daemon-reload</code> to reload the systemd daemon</p><p>after you create the quadlet. You have to do this every time, I won&#x27;t repeat</p><p>this throughout the blog post.</p><h3>Pod Quadlets</h3><p>You can also use quadlets to manage pods. Here&#x27;s an example of a pod quadlet.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Pod]</span></span></code></pre><p>Yep, that&#x27;s it. If you put it in <code>~/.config/containers/systemd/mypod.pod</code> you can</p><p>start it with <code>systemctl --user start mypod-pod</code> and it will start a new pod.</p><p>But the best part is this. If you want to add a container to a pod all you have</p><p>to do is this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/library/alpine:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sleep infinity</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Pod</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">mypod.pod</span></span></code></pre><p>Now when you start <code>mypod-pod</code> it will start the container as well.</p><h3>Dependencies</h3><p>You can add dependencies.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Unit]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">After</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">alpine.service</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/library/archlinux:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sleep infinity</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Pod</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">mypod.pod</span></span></code></pre><p>Now archlinux will start after alpine.</p><p>There are even more options. You can start .kube, .network, and .build quadlets</p><p>too and they can depend on each other. See the</p><p><a href="https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html">docs</a></p><p>for more info!</p><p>---</p><h3>Example: Nginx and Certbot</h3><p>Okay so now lets run something useful :)</p><p>How about an nginx container and a certbot container to get a certificate for it?</p><h3>Nginx Container</h3><p>First lets create a directory to hold our files.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ~/.config/containers/systemd/www</span></span></code></pre><p>Next we create a quadlet for our nginx container.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/nginx:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:80</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:443</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">./www:/usr/share/nginx/html</span></span></code></pre><p>This will run a web server for us. If you aren&#x27;t root, you&#x27;ll need to enable unprivleged ports.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sysctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> net.ipv4.ip_unprivileged_port_start=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span></span></code></pre><p>Now we can start it with <code>systemctl --user start nginx</code> and it will start a new container.</p><p>If you browse to <code>http://localhost</code> you should see the nginx welcome page.</p><h3>Certbot Container</h3><p>Now we need to get a certificate for it. We can use certbot for this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/certbot/certbot:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/letsencrypt</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">./www:/mnt</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">certonly --webroot --webroot-path /mnt --agree-tos --email your@email.com -d your.domain.com</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span></code></pre><p>Make sure to replace your@email.com and your.domain.com with your own email and domain.</p><p>Then start the container with <code>systemctl --user start certbot</code> and it will get a certificate for you.</p><h3>Nginx with SSL</h3><p>Now you can use the certificate in your nginx container.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/nginx:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:80</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:443</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/letsencrypt</span></span></code></pre><p>That&#x27;s great, but now we need to configure nginx to use the certificate. We can</p><p>do this by adding a volume for the configuration file.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/nginx:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:80</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:443</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/nginx/certs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">./nginx.conf:/etc/nginx/conf.d</span></span></code></pre><p>Along with a configuration file for nginx.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your.domain.com;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/certs/live/your.domain.com/fullchain.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/certs/live/your.domain.com/privkey.pem;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/usr/share/nginx/html;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        index </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">index.html;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><p>Again, make sure to replace your.domain.com with your own domain.</p><p>Now you can start the container with <code>systemctl --user start nginx</code> and it will</p><p>start a new container with the certificate. You can verify this by going to</p><p><code>https://your.domain.com</code>.</p><p>Now you have a web server with HTTPS.</p><h3>Automatic Certificate Renewal</h3><p>If you want to renew your certificate automatically it&#x27;s as simple as this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/certbot/certbot:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">renew</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/letsencrypt</span></span></code></pre><p>Now you can create a timer for it in <code>~/.config/systemd/user/certbot.timer</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Unit]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Renew Certbot certificates</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Timer]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">OnCalendar</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Sun </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">00</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:00:00</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Persistent</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Unit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">certbot.service</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Install]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">WantedBy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">timers.target</span></span></code></pre><p>Now you enable the timer.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">systemctl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --user</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --now</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> certbot.timer</span></span></code></pre><p>Now your certificate will be renewed every Sunday at midnight.</p><p>That&#x27;s it! Now you have a web server with HTTPS and a certificate that will be</p><p>renewed automatically.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/email-bot</guid>
		<title><![CDATA[Email bot]]></title>
		<description><![CDATA[Cloudflare email routing plus Workers AI: an inbox that answers its own mail.]]></description>
		<link>https://seanbehan.ca/posts/email-bot</link>
		<pubDate>Wed, 04 Dec 2024 20:29:41 GMT</pubDate>
		<category>cloudflare</category><category>serverless</category><category>typescript</category>
		<content:encoded><![CDATA[<h3>How it Works</h3><p>In this post I&#x27;m just going to show you something cool I made using cloudflare workers.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { EmailMessage } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'cloudflare:email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { createMimeMessage } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'mimetext'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">env</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> content</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message.raw).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> messages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			{ role: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'system'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, content: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'you are an email responder, reply to the given message'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			{ role: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, content }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		];</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">		// @ts-expect-error broken bindings</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">AI</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@cf/meta/llama-3.2-11b-vision-instruct'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, { messages });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> msg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createMimeMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setHeader</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'In-Reply-To'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message.headers.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Message-ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">as</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Sean Behan'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, addr: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'contact@seanbehan.ca'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setRecipient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message.from);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSubject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'RE'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			contentType: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text/plain'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			data: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`${</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">response</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">\n\n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">This was an automated message. If this is urgent, message Sean on Telegram https://t.me/codebam`</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> replyMessage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> EmailMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'contact@seanbehan.ca'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message.from, msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">asRaw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(replyMessage);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forward</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'codebam@riseup.net'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">} </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">satisfies</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ExportedHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Env</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>;</span></span></code></pre><p>This small amount of code is all it takes to automatically reply to all my emails with llama3.2!</p><p>Feel free to copy it and use it yourself. The source code is on <a href="https://github.com/codebam/email-bot">GitHub</a>.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/nixos</guid>
		<title><![CDATA[NixOS Flakes]]></title>
		<description><![CDATA[What a flake actually is: inputs, outputs, and a NixOS configuration with every dependency pinned.]]></description>
		<link>https://seanbehan.ca/posts/nixos</link>
		<pubDate>Sat, 01 Jun 2024 22:38:41 GMT</pubDate>
		<category>nixos</category>
		<content:encoded><![CDATA[<h3>Introduction to Flakes</h3><p>Flakes in NixOS allow you to write Nix code that has custom inputs and outputs.</p><p>For example this is a simple example of a Flake. You can get a copy for</p><p>yourself by running <code>nix flake init</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "A very basic flake"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  inputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    nixpkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "github:nixos/nixpkgs?ref=nixos-unstable"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  outputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nixpkgs }: {</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    packages</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">x86_64-linux</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hello</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">legacyPackages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">x86_64-linux</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hello</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    packages</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">x86_64-linux</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">default</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">packages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">x86_64-linux</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hello</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><h3>Flake Structure</h3><p>As you can see there are inputs (nixpkgs), and outputs (self, nixpkgs).</p><h3>Managing Dependencies with flake.lock</h3><p>If you build this Flake using <code>nix build</code> you&#x27;ll end up with a new directory</p><p>called <code>result</code> which is a symlink to where the package was built in your Nix</p><p>store.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">result</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> -</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /nix/store/yh6x9ia5kxxw3w90vkb09vqgfvhb416k-hello-2.12.1</span></span></code></pre><p>You&#x27;ll also end up with a <code>flake.lock</code>. If you&#x27;ve used <code>npm</code> before</p><p><code>flake.lock</code> is very similar to <code>package-lock.json</code>. If you haven&#x27;t, a quick</p><p>explanation is that it keeps track of package versions and keeps them locked</p><p>between updates. If you want to update your lockfile so you can get a newer</p><p>package version you can do so with <code>nix flake update</code></p><p>Now so far I&#x27;ve only showed a very basic example of a Flake, the example from</p><p><code>nix flake init</code>. You can see more templates with <code>nix flake show templates</code>.</p><h3>Development Shells (devShell)</h3><p>One thing I like using Flakes for is to create a <code>devShell</code>. You can initialize</p><p>a Flake with a <code>devShell</code> preconfigured with `nix flake init -t</p><p>templates#utils-generic`</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  inputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    utils</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "github:numtide/flake-utils"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  outputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> utils }: </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">utils</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">eachDefaultSystem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (system:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">legacyPackages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">${</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    in</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      devShell</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mkShell</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        buildInputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> with</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><p>In this example you can add packages from nixpkgs to <code>buildInputs</code> for them to</p><p>be installed temporarily. You can activate it with <code>nix develop</code>. This is the</p><p><code>devShell</code> I use for my typescript projects.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># ...</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">      devShell</span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mkShell</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        buildInputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> with</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; [</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          efm-langserver</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nil</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">nodejs</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">typescript-language-server</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">prettier</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          vscode-langservers-extracted</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">bash-language-server</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      }</span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># ...</span></span></code></pre><h3>Building Packages with Flakes</h3><p>You can use Flakes to build packages as well. You can look at just about any</p><p>Nix package for an example. Just choose one in</p><p><a href="https://github.com/nixos/nixpkgs">nixpkgs</a>. Flakes have many functions to</p><p>build different types of packages as you can see in nixpkgs.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/cf-workers-telegram-bot</guid>
		<title><![CDATA[Cloudflare Workers Telegram Bot]]></title>
		<description><![CDATA[A tour of the rewritten cf-workers-telegram-bot API for running a Telegram bot on Cloudflare Workers.]]></description>
		<link>https://seanbehan.ca/posts/cf-workers-telegram-bot</link>
		<pubDate>Wed, 15 May 2024 07:11:06 GMT</pubDate>
		<category>cloudflare</category><category>typescript</category>
		<content:encoded><![CDATA[<p>This past week I spent rewriting</p><p><a href="https://github.com/codebam/cf-workers-telegram-bot">`cf-workers-telegram-bot`</a>.</p><p>Here I&#x27;m going to show you the newly updated API that anyone can use to run a</p><p>Telegram Bot on Cloudflare Workers or any other serverless platform.</p><h3>Project Setup</h3><p>First you need to set up a new Cloudflare Workers project using wrangler.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> cloudflare@latest</span></span></code></pre><p>This will prompt you for a project name first. Choose a name for your new</p><p>project or use the default.</p><p>Next you&#x27;ll be prompted for the type of application you want to deploy. You</p><p>should choose a &quot;Hello World&quot; Worker.</p><p>After that you&#x27;ll be asked a few more questions, like if you want to use</p><p>TypeScript, and Git (optional, but recommended). You don&#x27;t need to deploy just</p><p>yet.</p><h3>Adding Your Bot Token</h3><p>Next you can add the token you get from <code>@BotFather</code> on Telegram as a secret so</p><p>you don&#x27;t have to keep it in the code.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npx</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> secret</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> put</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TOKEN</span></span></code></pre><p>Once you&#x27;ve created your project and created your secret you&#x27;ll want to install</p><p>the <code>cf-workers-telegram-bot</code> library to start coding.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> your-new-project</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> i</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> @codebam/cf-workers-telegram-bot</span></span></code></pre><p>This will give you access to my <code>TelegramBot</code> class for creating a Telegram Bot.</p><h3>Writing the Bot</h3><p>To get started you can open <code>src/index.ts</code> in your favorite editor such as</p><p>vscode or vim. Then you can import like this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TelegramBot, { TelegramExecutionContext } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@codebam/cf-workers-telegram-bot'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre><p>Then you can simply write your bot like this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fetch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">request</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Request</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">env</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Env</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ExecutionContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Promise</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> bot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TelegramBot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">TOKEN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> bot.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'start'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">context</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TelegramExecutionContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      switch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (context.update_type) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	case</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'message'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	  await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Hello from Cloudflare workers'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	  break</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	  break</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clone</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ok'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre><h3>Deployment and Webhook</h3><p>Now you&#x27;re ready to deploy.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npx</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> deploy</span></span></code></pre><p>You can set your webhook by going to the URL you&#x27;ve been given plus your token and <code>?command=set</code>.</p><p>Like this. <code>https://telegram-bot.username.workers.dev/YOURTOKENHERE?command=set</code></p><p>Now when you send your bot a message on Telegram it should respond with <code>Hello from Cloudflare workers</code>.</p><h3>Starter Template</h3><p>If you&#x27;ve got this far and you don&#x27;t want to code it all by yourself, you can</p><p>fork my repository <a href="https://github.com/codebam/cwtb-consumer">here</a> to get</p><p>started with a working copy.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/svelte-search</guid>
		<title><![CDATA[Search bar in Sveltekit]]></title>
		<description><![CDATA[The fuzzy search on this site, built with fuse.js.]]></description>
		<link>https://seanbehan.ca/posts/svelte-search</link>
		<pubDate>Wed, 06 Dec 2023 20:29:41 GMT</pubDate>
		<category>cloudflare</category><category>javascript</category><category>serverless</category><category>svelte</category><category>sveltekit</category><category>typescript</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>In this post I&#x27;m going to show you how I made the search bar on the posts page</p><p>and main page of my website.</p><h3>Implementing the Search Bar</h3><p>I used a library called <code>fuse.js</code> for fuzzy searching for titles, here&#x27;s how it</p><p>works.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">script lang</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { Heading } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'flowbite-svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Post </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/components/Post.svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Fuse </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'fuse.js'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { afterUpdate } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> posts</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">meta</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">date</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } }[];</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> query </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '*'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> posts;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> options</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { keys: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'meta.title'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">] };</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> fuse</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Fuse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(posts, options);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	afterUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results_ </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> fuse.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">search</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		results </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results_.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">result</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ path: result.item.path, meta: result.item.meta }));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	});</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">script</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Heading</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"h4"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	style</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"display: inline"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ml-8 my-4 text-secondary dark:text-dark-secondary w-auto"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Posts</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Heading</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">input class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"text-secondary"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{query} </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ul</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	{#each results </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">as</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Post {post} </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	{</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">each}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	{#</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">length</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		{#each posts </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">as</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Post {post} </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		{</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">each}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	{</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ul</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span></code></pre><h3>How it Works</h3><p>You can see I initialize the query to <code>&#x27;*&#x27;</code>. All this really does is sets the</p><p>search field to <code>*</code> instead of being empty. Then in the input element I bind to</p><p>the value of query so that when the user types in the field the value of query</p><p>will change.</p><p>I do some more initialization of <code>fuse.js</code> to search and then I register</p><p><code>afterUpdate()</code>. This allows me to search only when the page changes.</p><p>Finally I added some logic to display all the posts if there are no results,</p><p>like for example when the page loads.</p><p>All this together looks like the search bar you see on <code>/posts</code> and <code>/</code>.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/svelte-auth</guid>
		<title><![CDATA[Auth and Databases in Sveltekit]]></title>
		<description><![CDATA[HTTP Basic Auth with a session cookie in SvelteKit, backed by Cloudflare D1.]]></description>
		<link>https://seanbehan.ca/posts/svelte-auth</link>
		<pubDate>Sun, 12 Nov 2023 05:40:33 GMT</pubDate>
		<category>cloudflare</category><category>javascript</category><category>serverless</category><category>svelte</category><category>sveltekit</category><category>typescript</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>Recently I implemented HTTP Basic Auth using a session cookie in SvelteKit on</p><p>Cloudflare Pages using Cloudflare D1 as a database backend. Here I&#x27;ll show some</p><p>code snippets so you can do the same.</p><h3>Project Setup</h3><p>First you would want to set up your project according to</p><p><a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site">this</a>.</p><p>So you can have a basic project set up.</p><h3>Database Setup</h3><p>Next you&#x27;ll want to add the database to your project. It&#x27;s easy to create using</p><p>wrangler. Just remember to link it to your project in your cloudflare dash</p><p>under settings &gt; functions.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> your-database-name</span></span></code></pre><p>Then you can copy it into your</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/wrangler.toml">`wrangler.toml`</a></p><p>so your local dev works with it. To run with support for D1 you can use this</p><p>oneliner.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pages</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> .svelte-kit/cloudflare</span></span></code></pre><p>My final schema looks like this. Yours might be a bit different, but mine</p><p>allows for adding songs to a playlist, which was the purpose of</p><p><a href="https://github.com/codebam/svelte-auth">that project</a>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IF</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> EXISTS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Users (id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TEXT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IF</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> EXISTS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Playlist (id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TEXT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, email </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">date</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre><p>You&#x27;ll want to create these tables using wrangler both locally (on your dev</p><p>server) and remotely.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> execute</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> svelte-auth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --local</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --file=./schema.sql</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> execute</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> svelte-auth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --file=./schema.sql</span></span></code></pre><p>Now you can add it to your</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/src/app.d.ts">`app.d.ts`</a></p><p>like this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">declare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> global {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	namespace</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> App</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Platform</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">			env</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">				DB</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> D1Database</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			};</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">			context</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">				waitUntil</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">promise</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Promise</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			};</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">			caches</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheStorage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> &#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">default</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Cache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {};</span></span></code></pre><h3>Authentication Implementation</h3><p>Now you&#x27;re ready to set up authentication. It&#x27;s probably easier to just show</p><p>you how I did it. I imported <code>sha256</code> which is just a simple function I wrote</p><p>to call web crypto to get the sha256sum of a string.</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/login/%2Bpage.server.ts">Here</a></p><p>is my code.</p><p>I added <a href="https://kit.svelte.dev/docs/form-actions">form actions</a>. These</p><p>allow you to quickly write code that will work with HTML <code>&lt;form&gt;</code>&#x27;s.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { redirect } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@sveltejs/kit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sha256 </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/sha256'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> actions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> email</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sha256</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'password'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">??</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'INSERT INTO Users VALUES (?, ?)'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(email, password)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (success) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stringify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ email, password }));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	},</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	login</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> email</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sha256</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'password'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">??</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> results</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'SELECT password FROM Users WHERE id=?'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(email)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (results.results[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">].password </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> password) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stringify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ email, password }));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre><h3>Checking Authentication Status</h3><p>Now this is enough to save a cookie with the user auth, but to re-auth on a</p><p>page we need to check if their cookie is valid. So how I do this is I wrote</p><p>another function called</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/src/lib/tryLogin.ts">`tryLogin.ts`</a>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { D1Database } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cloudflare/workers-types'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">session_cookie</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> undefined</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">DB</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> D1Database</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (session_cookie) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">password</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">parse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(session_cookie);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> results</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'SELECT password FROM Users WHERE id=?'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(email).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (results.results[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">].password </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> password) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tryLogin;</span></span></code></pre><p>Now I can call this inside</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.server.ts">`+page.server.ts`</a>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tryLogin </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/tryLogin'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> getCurrentSong </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/getCurrentSong'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	auth: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	song: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getCurrentSong</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre><p>Using it inside the</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.svelte">`+page.svelte`</a></p><p>is as simple as this one line.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>export let data: {auth: boolean, song: {id: string, url: string}};</span></span></code></pre><h3>Playlist Management</h3><p>Now I wrote more form actions for adding and removing songs from the playlist</p><p><a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/playlist/%2Bpage.server.ts">here</a>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { redirect } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@sveltejs/kit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tryLogin </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/tryLogin'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> actions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> session</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">parse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> email</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> session.email;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> formdata</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> v</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> URL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(formdata.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'url'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)).searchParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'v'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'INSERT INTO Playlist VALUES (?, ?, ?, ?)'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">				.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(crypto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">randomUUID</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), email, v, Math.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">floor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Date</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTime</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">				.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	},</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	remove</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> formdata</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> formdata.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'DELETE FROM Playlist WHERE id=?'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		}</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre><h3>User Interface</h3><p>Now this allows us to add songs, we just need the interface for it. So I wrote</p><p>the <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.svelte">main page</a></p><p>to allow logged in users to submit songs.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { enhance } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$app/forms'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> YouTube </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/svelte-youtube.svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">auth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">song</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{#</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">data.auth}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Visit &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> href</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/login"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>login&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">YouTube</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">end</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> document.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getElementById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'remove_song'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()}</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	videoId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={data.song.url}</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	options</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={{ playerVars: { autoplay: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } }}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"display: none;"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"POST"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> action</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/playlist?/remove"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> use</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">enhance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">input</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"id"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">={data.song.id} /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"remove_song"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>remove&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{#</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data.auth}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"POST"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> action</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/playlist?/add"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> use</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">enhance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">label</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			>Submit a song</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">input</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"url"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"url"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> placeholder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"paste a youtube link"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">".*v=.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">label</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Submit&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> href</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/logout"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Log out&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><h3>Conclusion</h3><p>If you have any problems or you want to see the entire repo, the code is</p><p><a href="https://github.com/codebam/svelte-auth">here</a>.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/llama2</guid>
		<title><![CDATA[Using Llama2 on Cloudflare Workers]]></title>
		<description><![CDATA[Wiring Workers AI into a Telegram bot: the AI binding, the model call, and getting a reply back.]]></description>
		<link>https://seanbehan.ca/posts/llama2</link>
		<pubDate>Thu, 12 Oct 2023 18:21:27 GMT</pubDate>
		<category>cloudflare</category><category>serverless</category><category>typescript</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>You might know I&#x27;ve made a Chat Bot</p><p><a href="https://github.com/codebam/cf-workers-telegram-bot">cf-workers-telegram-bot</a>.</p><p>Recently I added AI to it using Cloudflare AI. Here I&#x27;m going to show you how</p><p>and how you too can make your own chatbot using Cloudflare AI.</p><h3>Cloudflare Workers AI Setup</h3><p>First you&#x27;ll want to add the AI section to your wrangler.toml.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">\[ai\]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">binding = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"AI"</span></span></code></pre><h3>Using the AI Model</h3><p>Then you can simply import</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { Ai } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cloudflare/ai'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre><p>And you can use it like this</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> ai</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Ai</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">AI</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">prompt </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'hello world'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ai.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@cf/meta/llama-2-7b-chat-int8'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	prompt</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre><h3>Other Models and Tips</h3><p>That&#x27;s it!</p><p>There are other models you can use as well on Cloudflare AI so you should check</p><p>them out here for other cool things you can do</p><p>https://developers.cloudflare.com/workers-ai/models/</p><p>I&#x27;m hoping for a 70B model soon :)</p><p>If you want to use this to make a chat bot make sure when you restore the</p><p>prompt you wrap user input in <code>[INST] [/INST]</code> tags so llama2 knows what was</p><p>user input and what it said.</p><p>If you want to try out my Chat Bot it&#x27;s on <a href="https://t.me/TuxRobot">Telegram</a>.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/ostree</guid>
		<title><![CDATA[Using Ostree for file snapshots]]></title>
		<description><![CDATA[Ostree is not just for Silverblue. Using it as a content-addressed snapshot store for your own files.]]></description>
		<link>https://seanbehan.ca/posts/ostree</link>
		<pubDate>Sat, 27 May 2023 11:47:26 GMT</pubDate>
		<category>backup</category><category>linux</category><category>ostree</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>Ostree is the software that allows rpm-ostree in Fedora Silverblue to keep snapshots of previous deployments and do incremental updates, but did you know it can also be used to keep snapshots of your own files? It&#x27;s actually quite easy once it&#x27;s all set up.</p><p>For this I would strongly suggest you use root for your ostree. Although you can use it without, you will be able to use files that regular users can&#x27;t use such as hardlinks if you&#x27;re root, which will speed up incremental snapshots very much.</p><h3>Initializing the Ostree Repository</h3><p>First you&#x27;ll want to create your inital ostree. Do this in a new folder, on a drive with lots of free space for the files you want to snapshot. You&#x27;ll need as much free space as all the files you want to snapshot.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> tree</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> init</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span></span></code></pre><h3>Committing and Restoring Files</h3><p>Next you can start committing files to your new tree. Be sure to check the man pages for <code>ostree-commit</code> and <code>ostree-init</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> commit</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --branch=master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $PWD</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/dir</span></span></code></pre><p>Keep in mind you cannot commit individual files, only entire directories at a time. Now when you change something inside <code>/dir</code> you can commit again with the exact same command and you will have both copies in your ostree. To restore the original copy you can then use <code>ostree-checkout</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> refs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> master</span></span></code></pre><p>This will show you a list of all your commits. Now choose the one you want to restore.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> checkout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --union</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> your-commit-sha</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dir</span></span></code></pre><h3>Important Considerations</h3><p>Make sure you check <code>ostree -h</code> for a list of ostree commands and read each of their man pages if you want to use them fully.</p><p>I use this on a filesystem that is already a RAID1. Make sure you keep proper backups, ostree can create snapshots but it won&#x27;t help you if your entire drive fails.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/flatpak-mesa</guid>
		<title><![CDATA[Flatpak Mesa Git]]></title>
		<description><![CDATA[Swapping the Flatpak Mesa runtime for mesa-git so a new AMD card stops falling back to software OpenGL.]]></description>
		<link>https://seanbehan.ca/posts/flatpak-mesa</link>
		<pubDate>Thu, 25 May 2023 17:48:28 GMT</pubDate>
		<category>graphics</category><category>linux</category>
		<content:encoded><![CDATA[<h3>The Problem</h3><p>If you&#x27;ve switched to Flatpak for gaming you may notice that Mesa is sometimes a little bit old. The issue I had myself was that my 6000 series AMD graphics card didn&#x27;t have up to date graphics drivers, resulting in OpenGL games not running on the graphics card, but rather the CPUs OpenGL implementation, which made games such as CS:GO and Minecraft run at 5 or 10 FPS.</p><h3>The Solution</h3><p>To get the latest Mesa git on Flatpak you can install it like this:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> org.freedesktop.Platform.GL.mesa-git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> org.freedesktop.Platform.GL32.mesa-git</span></span></code></pre><p>This will allow you to install the latest Mesa graphics drivers to allow applications to detect hardware properly and run better. Make sure when you are prompted for a version of Mesa to install you choose the latest version number that doesn&#x27;t have &quot;beta&quot; at the end.</p><h3>Usage</h3><p>Once you&#x27;ve done this you can run your Flatpak like this to use Mesa git.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">FLATPAK_GL_DRIVERS</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mesa-git</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.valvesoftware.Steam</span></span></code></pre><p>You can also export <code>FLATPAK_GL_DRIVERS=mesa-git</code> in your <code>.bashrc</code> or <code>.profile</code> to use Mesa git for all Flatpak applications. This may cause issues though, and I&#x27;d personally advise you only use it when it&#x27;s needed.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/btrfs</guid>
		<title><![CDATA[BTRFS RAID1 and how to fix it]]></title>
		<description><![CDATA[Setting up a two-disk BTRFS RAID1 for a Steam library, and repairing it when a drive starts throwing errors.]]></description>
		<link>https://seanbehan.ca/posts/btrfs</link>
		<pubDate>Tue, 23 May 2023 14:39:58 GMT</pubDate>
		<category>backup</category><category>linux</category><category>raid</category>
		<content:encoded><![CDATA[<h3>BTRFS RAID1 Setup</h3><p>I mentioned in my previous post that I use an external drive to keep my Steam</p><p>games on. In an attempt to not have to re-download hundreds of gigabytes of</p><p>games I keep them on a RAID1 with 2x5TB drives that I use BTRFS to manage. In</p><p>this post I&#x27;m going to outline how I set it all up, and how I manage it when</p><p>issues arise.</p><h3>Formatting Drives</h3><p>First of all I had to format the drives, so I made sure they were empty and</p><p>everything was backed up. Don&#x27;t do this unless you know for sure that all the</p><p>data on all your drives is backed up somewhere else, or you want to delete it</p><p>permanently.</p><p>Okay so the commands are easy, you just have to make sure you&#x27;re using the</p><p>right ones as not to lose your data. Don&#x27;t run these commands without</p><p>understanding what they do. Replace \_ with your drive letter. A lot of these</p><p>commands need to be run with root, make sure to either elevate to root (<code>sudo -s</code>)</p><p>or prepend the commands with <code>sudo</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsblk</span></span></code></pre><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkfs.btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkfs.btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span></code></pre><h3>Creating the RAID1 Array</h3><p>Now you can mount the newly formatted drives to a single RAID1.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mount</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> device</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -dconvert=raid1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mconvert=raid1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre><p>The last command might take a little bit of time, but once it&#x27;s finished your</p><p>drives will be all ready to be used.</p><h3>Managing the Array</h3><p>Now if you need to swap out a drive, add another, or you unplug one when it&#x27;s</p><p>writing you can simply balance again and btrfs should be able to balance back</p><p>onto the drive.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -dconvert=raid1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mconvert=raid1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre><p>You can view the status of the balance using:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> status</span></span></code></pre><p>You can remove a drive or add another using:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> device</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> remove</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> device</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span></code></pre><p>Just make sure you balance right afterwards (which will take a VERY long time</p><p>if you have lots of data, beware)</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -dconvert=raid1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mconvert=raid1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/flatpak</guid>
		<title><![CDATA[Using any Linux Distribution with Flatpak]]></title>
		<description><![CDATA[Flatpak makes the host distribution almost irrelevant, down to proprietary codecs and games on musl Alpine.]]></description>
		<link>https://seanbehan.ca/posts/flatpak</link>
		<pubDate>Tue, 23 May 2023 13:49:32 GMT</pubDate>
		<category>graphics</category><category>linux</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>I was using Fedora Silverblue for nearly a year before I realized I had been using Flatpak&#x27;s for nearly all my software, which mind you isn&#x27;t much, but I can use Firefox, Chrome, and play any video game I want using Flatpak alone so it makes sense for me.</p><p>To get this working all you need is a distribution where you can install Flatpak. I&#x27;m using Alpine Linux edge with musl and even that works. Using Flatpak allows me to use proprietary video and audio codecs in Firefox and Chrome since musl doesn&#x27;t support those codecs as of right now.</p><h3>Setting up Flatpak</h3><p>If your distribution doesn&#x27;t already come with FlatHub like Alpine, you&#x27;ll need to <a href="https://flatpak.org/setup/">add it</a>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> remote-add</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --if-not-exists</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> flathub</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://flathub.org/repo/flathub.flatpakrepo</span></span></code></pre><h3>Installing Applications</h3><p>This will allow you access to the entire Flatpak library. You can search for software using:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> search</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> steam</span></span></code></pre><p>Then install using:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.valvesoftware.Steam</span></span></code></pre><p>Make sure you periodically update your Flatpak&#x27;s as well using:</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span></span></code></pre><h3>Steam and Gaming</h3><p>For Steam to work you&#x27;ll likely need to install some other Flatpak&#x27;s as well, but once they&#x27;re installed all your other gaming Flatpak&#x27;s should &quot;just work&quot;.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.valvesoftware.Steam.CompatibilityTool.Proton</span></span></code></pre><p>You might also want to install your distribution&#x27;s <code>steam-devices</code> package if you want to use a game controller.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> steam-devices</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> # ubuntu / debian</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">doas</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apk</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> steam-devices</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         # alpine</span></span></code></pre><h3>Managing Permissions with Flatseal</h3><p>I like to give the Steam Flatpak access to my external drives so I can keep games there instead of on my hard drive. To do this I simply install Flatseal and use Flatseal to give rw (read+write) access to the directory where my drives mount to.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.github.tchx84.Flatseal</span></span></code></pre><h3>Google Chrome</h3><p>Next if you want to install Google Chrome.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.google.Chrome</span></span></code></pre><h3>Conclusion</h3><p>That&#x27;s all you have to do, then you would have Google Chrome installed.</p><p>Flatpak uses it&#x27;s own runtime, remember to update all your Flatpak&#x27;s periodically with <code>flatpak update</code> to avoid security vulnerabilities and other issues.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/8814au</guid>
		<title><![CDATA[Fedora 8814au Kernel Module Compilation]]></title>
		<description><![CDATA[Compiling the out-of-tree 8814au driver for an Alfa AC1900 on Fedora, kernel headers and all.]]></description>
		<link>https://seanbehan.ca/posts/8814au</link>
		<pubDate>Thu, 12 May 2022 19:21:37 GMT</pubDate>
		<category>development</category><category>kernel</category><category>linux</category>
		<content:encoded><![CDATA[<p>For my wireless card, the Alfa AC1900, I had to compile the module to get it to</p><p>work on Linux since it wasn&#x27;t already part of the kernel.</p><h3>Prerequisites</h3><p>To do this you will need:</p><ol><li><code>make</code> and <code>gcc</code> or <code>clang</code></li><li><code>kernel-debug-devel</code> package</li><li>sudo permission to <code>insmod</code> the compiled module</li></ol><p>This[^1] is the only one I found that worked for me on the latest stable linux</p><p>kernel, which as of today on Fedora is 5.17.6.</p><h3>Kernel Header Installation</h3><p>First you need to install the kernel development header package.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span># Fedora</span></span>
<span class="line"><span>sudo dnf install kernel-debug-devel</span></span>
<span class="line"><span></span></span>
<span class="line"><span># Ubuntu</span></span>
<span class="line"><span>sudo apt install linux-headers-$(uname -r)</span></span></code></pre><p>[^2]</p><h3>Building and Loading the Module</h3><p>Next you need to make the module.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>git clone https://github.com/morrownr/8814au.git</span></span>
<span class="line"><span>cd 8814au</span></span></code></pre><p>Now simply <code>make</code> the module using the <code>make</code> command and use <code>insmod</code> to load</p><p>it.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>make -j16</span></span>
<span class="line"><span>sudo sh -c "modprobe cfg80211; insmod /home/codebam/8814au/8814au.ko"</span></span></code></pre><p>If everything worked successfully, you should now have a loaded 8814au module</p><p>that you can use along with your wireless card to connect to WiFi.</p><p>[^1]: https://github.com/morrownr/8814au</p><p>[^2]: https://www.tecmint.com/install-kernel-headers-in-ubuntu-and-debian</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/x11docker</guid>
		<title><![CDATA[Using X11Docker for secure GUI applications]]></title>
		<description><![CDATA[Running GUI applications like telegram-desktop inside a container with x11docker, xpra and Wayland.]]></description>
		<link>https://seanbehan.ca/posts/x11docker</link>
		<pubDate>Thu, 07 Oct 2021 20:51:51 GMT</pubDate>
		<category>docker</category><category>graphics</category><category>podman</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>X11Docker is a nice command line tool that allows you to run graphical</p><p>applications inside podman or docker by passing them to xpra or your wayland</p><p>socket. To do this you first need a <code>Containerfile</code> or <code>Dockerfile</code> to build an</p><p>image with the application you want to run. In this example I&#x27;m going to run</p><p><code>telegram-desktop</code>.</p><h3>Building a Container Image</h3><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>FROM ubuntu:latest</span></span>
<span class="line"><span></span></span>
<span class="line"><span>RUN apt-get update</span></span>
<span class="line"><span>RUN apt-get install -y telegram-desktop</span></span>
<span class="line"><span></span></span>
<span class="line"><span>CMD ["telegram-desktop"]</span></span></code></pre><h3>Running the Application with X11Docker</h3><p>We build this image with <code>podman build . -t telegram</code> and wait for it to</p><p>install our packages. When it&#x27;s done we can simply run it with `x11docker</p><p>localhost/telegram` and it starts Telegram in Xpra.</p><h3>Running with Wayland</h3><p>If we wanted to start it under Wayland instead, we could run `x11docker</p><p>--wayland localhost/telegram` however the package for Telegram on Ubuntu</p><p>doesn&#x27;t seem to have support for Wayland when I tried it.</p><p>This should work for any graphical software, even that which isn&#x27;t in the</p><p>repositories. You can even install more than one piece of software in a single</p><p>image. Then you pass the CMD as an argument to x11docker like so `x11docker</p><p>localhost/telegram telegram-desktop`.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/podman-systemd</guid>
		<title><![CDATA[Podman Auto Updating Systemd Services]]></title>
		<description><![CDATA[podman generate systemd plus an autoupdate label: containers that start on boot and update themselves.]]></description>
		<link>https://seanbehan.ca/posts/podman-systemd</link>
		<pubDate>Thu, 23 Sep 2021 14:34:43 GMT</pubDate>
		<category>docker</category><category>linux</category><category>podman</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>Podman[^1] has a nice feature called <code>generate</code> that lets you generate</p><p>different kinds of configuration files for starting pods. One that I use often</p><p>is <code>podman generate systemd</code> which generates a new user service that can be</p><p>started with <code>systemctl --user start container-yourcontainer.service</code> or</p><p>enabled.</p><h3>Auto-Updating Containers</h3><p>If you want your container to auto update you can simply pass it the flag</p><p><code>--label &quot;io.containers.autoupdate=registry&quot;</code> when you&#x27;re creating it. The</p><p>systemd service will automatically pull the latest versions of containers</p><p>before starting them. You can manually update your container images now using</p><p><code>podman auto-update</code>.</p><p>If you want to update your images on a schedule you can enable the</p><p><code>podman-auto-update.timer</code> using `systemctl --user enable --now</p><p>podman-auto-update.timer` which will auto update your containers every Monday</p><p>morning.[^2]</p><h3>Kubernetes YAML Generation</h3><p>You can also generate a Kubernetes YAML configurations using `podman generate</p><p>kube`. This can then be loaded in Kubernetes to run your containers.</p><p>[^1]: https://podman.io</p><p>[^2]: https://fedoramagazine.org/auto-updating-podman-containers-with-systemd/</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/podman-crun</guid>
		<title><![CDATA[Podman crun and cgroups v2]]></title>
		<description><![CDATA[Getting Podman onto cgroups v2 and crun on distributions that still default to cgroups v1 and runc.]]></description>
		<link>https://seanbehan.ca/posts/podman-crun</link>
		<pubDate>Sun, 19 Sep 2021 03:09:02 GMT</pubDate>
		<category>docker</category><category>linux</category><category>podman</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>Podman[^1] is a container management software similar to Docker that can run</p><p>OCI containers as regular users (not root) by using container management</p><p>libraries such as crun or runc, and cgroups v1 or v2. It can be used as a</p><p>drop-in replacement for Docker and supports docker-compose using</p><p>podman-compose.</p><p>Unfortunately for some distributions after installing podman the operating</p><p>system defaults to cgroups v1 and runc, not cgroups v2 and crun. As was the</p><p>case on Rocky Linux for me.</p><h3>Switching to crun</h3><p>First we can switch to crun instead of runc by installing crun with our package</p><p>manager. It was already installed for me, but on Fedora you would use `sudo dnf</p><p>install crun`</p><p>Then you can edit the file <code>/usr/share/containers/containers.conf</code> and change</p><p>the line <code>runtime = &quot;runc&quot;</code> to <code>runtime = &quot;crun&quot;</code>. Now you can verify that</p><p>you&#x27;re using crun with <code>podman info | grep crun</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>name: crun</span></span>
<span class="line"><span>package: crun-0.20.1-1.module+el8.4.0+643+525e162a.x86_64</span></span>
<span class="line"><span>path: /usr/bin/crun</span></span>
<span class="line"><span>  crun version 0.20.1</span></span></code></pre><h3>Enabling cgroups v2</h3><p>Now the harder part was enabling cgroups v2. Luckily I found a</p><p><a href="https://github.com/containers/podman/issues/9410#issuecomment-785840320">comment</a></p><p>on a GitHub issue outlining the steps you need to complete to get cgroups v2 to</p><p>work.</p><p>First we can enable it in the kernel with</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"</span></span></code></pre><p>Then we can enable linger. This will also allow our containers to remain</p><p>running after logging out.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>sudo loginctl enable-linger codebam</span></span></code></pre><p>Then the last step which made everything work for me is to set the</p><p><code>pid_limit=0</code>. For me it was in <code>/usr/share/containers/containers.conf</code> instead</p><p>of <code>/etc/containers/</code>.</p><h3>Verification</h3><p>Now you can reboot. <code>podman info | grep cgroup</code> should say</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>cgroupControllers: []</span></span>
<span class="line"><span>cgroupManager: systemd</span></span>
<span class="line"><span>cgroupVersion: v2</span></span></code></pre><p>Now we&#x27;re using cgroups v2 and crun.</p><p>[^1]: https://podman.io</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/react-state</guid>
		<title><![CDATA[Managing State in React]]></title>
		<description><![CDATA[useState, what triggers a re-render, and where React state should actually live.]]></description>
		<link>https://seanbehan.ca/posts/react-state</link>
		<pubDate>Sat, 04 Sep 2021 20:31:25 GMT</pubDate>
		<category>javascript</category><category>programming</category><category>react</category>
		<content:encoded><![CDATA[<h3>Introduction to useState</h3><p>In React when we want to manage individual state there is a function we can</p><p>import called <code>useState</code> that gives us a getter and setter letting React know</p><p>when it needs to re-render.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> React, { useState } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'react'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> myComponent</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">setCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><p>This initializes count as a state variable that can be accessed with <code>count</code></p><p>and set with <code>setCount</code>.</p><h3>Incrementing a Counter</h3><p>If we wanted to write a function that incremented count, we could add a button.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Count </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">is</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}&#x3C;/</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">button onClick</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> + 1)}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>+</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">button</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre><p>Here we wrapped our text and button in a [React</p><p>Fragment](https://react.dev/reference/react/Fragment). Since components can only</p><p>return one DOM parent element, Fragments allow us to return more than one</p><p>without creating more divs in our markup.</p><p>Then I set the <code>onClick</code> for button to an inline function that increases count</p><p>by 1 using <code>setCount</code>. If we were to do this without a state variable, i.e.</p><p><code>const count = 0</code> React wouldn&#x27;t know when to re-render our data.</p><h3>Managing List State</h3><p>This can be used to hold any state or object however, not just numbers and</p><p>strings. If we wanted to instead append to a list we might write a function</p><p>that gets passed to the <code>onSubmit</code> of a form.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[list, setList] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useState</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([]);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> onFormSubmit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event) {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  event.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">preventDefault</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  list.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.name);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  setList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><h3>Handling Form Input</h3><p>Changing all our input <code>onChange</code> methods as well to update the state of <code>form</code></p><p>when the data changes.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">label</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">p className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"text-label"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:&#x3C;/</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">input</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    onChange</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">{handleSetForm}</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"text"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"name"</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  /></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">label</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span></code></pre><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[form, setForm] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useState</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleSetForm</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: { </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } }</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  setForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">form, [name]: value }));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre><p>This updates our form state whenever the data changes. Allowing us to use</p><p><code>form</code> to append to our list above whenever the submit button is clicked.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/react-card</guid>
		<title><![CDATA[React.js Card]]></title>
		<description><![CDATA[Building a reusable, nestable card component while learning how React composition works.]]></description>
		<link>https://seanbehan.ca/posts/react-card</link>
		<pubDate>Thu, 18 Feb 2021 00:27:04 GMT</pubDate>
		<category>css</category><category>javascript</category><category>programming</category><category>react</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>I&#x27;ve been learning React so that I can build my web applications for others and</p><p>myself. React is component based. You create reusable components and render</p><p>them with data. Here I created a card component that lets me insert cards. They</p><p>can even contain other cards and are easy to understand from the App.js.</p><h3>Creating the Card Component</h3><p>To create a new component I just create a new file called <code>Card.js</code> and import</p><p>it at the top of <code>App.js</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Card </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './Card.js'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre><p>Then inside <code>Card.js</code> I can create my Card. At the top I import my styles and</p><p>return the JSX Card object.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './Card.css'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Card</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">props</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Card"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">h2</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CardTitle"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>{props.cardTitle}&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">h2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CardContent"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>{props.children}&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Card;</span></span></code></pre><p>At the bottom I set the default export to Card so that we can import it</p><p>properly in <code>App.js</code>.</p><h3>Styling the Card</h3><p>My styles are fairly simple, they just create a nice looking card.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.Card</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	text-align</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">center</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	background-color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">lightgrey</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">px</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> solid</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> grey</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	margin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	height</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">20</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	overflow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hidden</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.CardTitle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	font-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.CardContent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	background-color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">white</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	height</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">%</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-top-left-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-top-right-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	padding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><h3>Using the Card Component</h3><p>That&#x27;s great but we still have to insert our card in App.js. Now that we&#x27;ve</p><p>created a component and imported it all I have to do in App.js to create a card</p><p>is this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">Card</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> cardTitle</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Example Card Title"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Hello World&#x3C;/</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">Card</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre><h3>Conclusion</h3><p>Finally we can see that this builds a card.</p><p>This can be used for building any kind of component, such as dialog boxes,</p><p>forms, or anything you can imagine in HTML/CSS.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/silverblue</guid>
		<title><![CDATA[Fedora Silverblue]]></title>
		<description><![CDATA[Why an atomic rpm-ostree desktop stuck: updates that check out like git, and roll back the same way.]]></description>
		<link>https://seanbehan.ca/posts/silverblue</link>
		<pubDate>Sat, 13 Feb 2021 20:21:43 GMT</pubDate>
		<category>linux</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>Over the past few months I experimented with some new Linux distributions. Here</p><p>I&#x27;ll show you the one I&#x27;m currently using and why I think it&#x27;s great.</p><h3>Atomic Updates and Rollbacks</h3><p>Fedora Silverblue is what&#x27;s known as an Atomic operating system. On Fedora</p><p>Silverblue every update is checked out similar to how git checks out a new</p><p>reference. If you want to install packages into the base image you can use</p><p>rpm-ostree install like you usually would, the only difference is that you need</p><p>to reboot to boot into your new base system reference.</p><h3>Toolbox for Development</h3><p>If you don&#x27;t want to reboot there is a better way though. Instead of layering</p><p>packages into your base image you can use a tool called toolbox which is simply</p><p>a wrapper around podman for creating containers with the current Fedora image.</p><p>You can use toolbox enter and install and run any software as well as access</p><p>files inside your home directory.</p><h3>Rebasing and Customization</h3><p>If at some point you do updates and your system doesn&#x27;t boot, you don&#x27;t have to</p><p>worry. You can simply reboot and choose the last working base image and your</p><p>system will boot up as normal, allowing you to fix any breakage.</p><p>Silverblue is so much more powerful than that though. You can rebase your</p><p>entire system on different base images or Fedora versions. Now instead of</p><p>installing your new desktop environment and risk breaking your operating system</p><p>you can do an <code>rpm-ostree rebase</code> and rebase on another desktop environment. I</p><p>used this to rebase on an image from</p><p><a href="https://fedoraproject.org/atomic-desktops/kinoite/">Kinoite</a> called</p><p>base which just includes all the base packages and no desktop environment. Then</p><p>I installed my window manager of choice and it&#x27;s dependencies. Now I have a</p><p>fully modular Fedora installation along with <a href="https://swaywm.org/">Sway</a> and</p><p>all the dependencies I need to use it.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/alpine-lbu</guid>
		<title><![CDATA[Alpine Linux LBU]]></title>
		<description><![CDATA[Alpine can commit changes back onto its own live ISO. Using LBU to build a persistent maintenance drive.]]></description>
		<link>https://seanbehan.ca/posts/alpine-lbu</link>
		<pubDate>Fri, 04 Dec 2020 03:09:46 GMT</pubDate>
		<category>alpine</category><category>linux</category>
		<content:encoded><![CDATA[<h3>What is LBU?</h3><p>Alpine Linux has a cool feature called LBU that lets you create a backup on top</p><p>of the Live ISO. These backups are called local backups.</p><p>I plan on using LBU as a system maintenance drive. It allows you to save</p><p>changes you make to the Live ISO and restore them automatically when you boot</p><p>the next time.</p><h3>Setup and Committing</h3><p>To set up LBU I booted the Alpine Linux ISO, ran <code>setup-alpine</code>, mounted my</p><p>drive to <code>/media/alpine_usb</code> and when I got to the disk selection I selected no</p><p>disk, and <code>alpine_usb</code> as where to store the configs, and the default for</p><p>cache.</p><p>Now when I make changes in the Alpine Linux Live ISO I can commit them.</p><p>To see what is being committed I first check <code>lbu status</code> which lists all the</p><p>files being added or removed, then <code>lbu commit</code> to commit the changes.</p><p>When I was rebooting I had an issue where the drive would automount itself to</p><p>/media/sdb and then wouldn&#x27;t work with lbu when I wanted to commit or check the</p><p>status because it was already mounted. To fix this I just had to `umount</p><p>/dev/sdb` on boot and then lbu worked as expected.</p><h3>Caching and Configuration</h3><p>To use cache you have to mount it to <code>/media/alpine_usb</code> so that it can write</p><p>the cache files when you&#x27;re installing packages. I want this because it makes</p><p>reinstalling my packages on reboot a lot faster, they can just be loaded from</p><p>cache instead of from online repositories.</p><p>Upon reboot I had to reinstall all the packages I had installed using `apk</p><p>upgrade` but since the cache is there it just installs everything really</p><p>quickly.</p><p>I uncommented and changed the number of backups in <code>/etc/lbu/lbu.conf</code> to 20 so</p><p>that I would be able to revert up to 20 commits in case I broke my configs.</p><p>I see Alpine LBU as a good way to keep a backup of changes to the ISO so it</p><p>could be used as a system recovery disk with the tools that are needed.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/2fa-everything</guid>
		<title><![CDATA[2FA Everything]]></title>
		<description><![CDATA[Moving a password store between pass and KeePass, and getting two YubiKeys to guard all of it.]]></description>
		<link>https://seanbehan.ca/posts/2fa-everything</link>
		<pubDate>Fri, 04 Dec 2020 01:38:10 GMT</pubDate>
		<category>linux</category><category>security</category>
		<content:encoded><![CDATA[<h3>Password Management</h3><p>I&#x27;ll start this post by saying one thing. Are you SURE you want to wipe that</p><p>yubikey? I spent the last 2 weeks redoing my entire password manager. Silly me</p><p>forgot that I had my passwords encrypted using the yubikey I wiped to create</p><p>new PGP keys.</p><p>Anyways over the past week or two I moved all my passwords from</p><p><a href="https://www.passwordstore.org/">pass</a> into <a href="https://keepass.info/">keepass</a></p><p>and then back into pass when keepass didn&#x27;t meet all my needs for a password</p><p>manager. KeePass supported using yubikeys for decryption, but not more than one</p><p>at a time. If I were to lose the key I would get permanently locked out.</p><p>Features I want in my password manager:</p><ul><li>2FA for password manager decryption</li><li>Mobile and desktop apps</li><li>Secure encryption</li></ul><p>What I settled on was this. I bought myself a [Yubikey 5C</p><p>NFC](https://www.yubico.com/ca/product/yubikey-5c-nfc/) to go alongside my</p><p>previous <a href="https://www.yubico.com/ca/product/yubikey-5c/">Yubikey 5C</a>. There</p><p>might be better alternatives, but this is the one I purchased. This one had NFC</p><p>and was convenient for accessing passwords on my phone and laptop.</p><h3>Yubikey PGP Key Generation</h3><p>To generate my PGP keys I actually generated them on the Yubikey itself, this</p><p>way the private key never touches the laptop I used to generate the key.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>$ gpg --card-edit</span></span>
<span class="line"><span>gpg/card> admin</span></span>
<span class="line"><span>gpg/card> key-attr</span></span>
<span class="line"><span>gpg/card> generate</span></span></code></pre><p>The <code>key-attr</code> command lets you choose the type and size of key you&#x27;re</p><p>generating on the device. I used RSA 4096 for my Yubikey 5C and ed25519 for the</p><p>Yubikey 5C NFC. I can&#x27;t use RSA or larger keys if I want to be able to use it</p><p>with NFC.</p><p>I backed up my revokal certificates that were put in the <code>~/.gnupg/</code> directory</p><p>as indicated after they were generated, and I exported and backed up the public</p><p>keys, then I uploaded the keys to a keyserver.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>gpg --export --armor 0F6D5021A87F92BA > 0F6D5021A87F92BA.asc</span></span>
<span class="line"><span>gpg --send-keys 0F6D5021A87F92BA</span></span>
<span class="line"><span></span></span>
<span class="line"><span>gpg --export --armor F9BC985B3BF972C7 > F9BC985B3BF972C7.asc</span></span>
<span class="line"><span>gpg --send-keys F9BC985B3BF972C7</span></span></code></pre><p>I started putting passwords into pass using `pass init 0F6D5021A87F92BA</p><p>F9BC985B3BF972C7`. That lets me encrypt to both Yubikeys at once. Now if I lose</p><p>or accidentally wipe one of the Yubikeys again the other will be able to</p><p>decrypt my passwords. I also signed the keys with each other, just to show that</p><p>both are valid if one were to be lost.</p><p>While I did all this I thought I might as well enable 2FA using both the</p><p>Yubikeys on every account that supported it. Google actually has something</p><p>cool called Advanced Protection which allows you to set up your account with 2</p><p>yubikeys to login with, so I enabled that for my Google account.</p><p>I downloaded the [password</p><p>store](https://play.google.com/store/apps/details?id=dev.msfjarvis.aps) Android</p><p>app for my phone, along with <a href="https://www.openkeychain.org/">Openkeychain</a>.</p><p>This part wasn&#x27;t too hard to set up, I just searched for my key on the</p><p>keyserver that I just uploaded it to, then imported it. I went through the</p><p>setup to add a security token and imported it so that it was recognized as one</p><p>of my keys.</p><h3>Git and Password Store Sync</h3><p>At this point I set up my password store with git to use for syncing. I set it</p><p>up on some mirrors just in case one went down, and I backed it up to Nextcloud.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>git remote add all git@seanbehan.dev:pass</span></span>
<span class="line"><span>git remote set-url --add --push all git@seanbehan.dev:pass</span></span>
<span class="line"><span>git remote set-url --add --push all git@git.sr.ht:~codebam/pass</span></span>
<span class="line"><span>git remote set-url --add --push all git@git.maych.in:codebam/password-store</span></span>
<span class="line"><span>git push --set-upstream all master</span></span></code></pre><p>I actually set up git on my own server here too. To do this I added a new user</p><p>named <code>git</code> and added my ssh keys to it, then inside it&#x27;s home directory I made</p><p>git repositories to push to using.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>mkdir pass</span></span>
<span class="line"><span>cd pass</span></span>
<span class="line"><span>git init --bare</span></span></code></pre><h3>Yubikey for SSH</h3><p>I generated a new SSH key from within the password store app and used that for</p><p>authentication from my phone. On my laptop I set up gpg-agent with ssh support</p><p>so that I could use my yubikey as an SSH key as well as a PGP key.</p><h3><code>~/.bashrc</code></h3><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> GPG_TTY</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tty</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)"</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SSH_AUTH_SOCK</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/run/user/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$UID</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/gnupg/S.gpg-agent.ssh"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">gpg-connect-agent</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> updatestartuptty</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /bye</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/null</span></span></code></pre><h3><code>~/.gnupg/gpg.conf</code></h3><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">use-agent</span></span></code></pre><p>Then I used <code>ssh-add -L</code> to show my public SSH key for each yubikey to put on</p><p>the server.</p><p>I used pass for 2FA as well because the password store app supports that. I</p><p>just scanned the code and installed <code>pass-otp</code> on Fedora to get support for</p><p>OTPs.</p><p>I installed the <a href="https://github.com/passff/passff">PassFF</a> Firefox extention to</p><p>get support for pass in my browser, and turned on autofill on my phone so that</p><p>it can autofill everywhere.</p><p>I set up git to sign all my commits so that it shows that my commits are signed</p><p>and valid.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>git config --global user.signingkey 0F6D5021A87F92BA</span></span>
<span class="line"><span>git config --global commit.gpgsign true</span></span></code></pre><h3>System-level U2F Authentication</h3><p>As an extra layer of security on Fedora I installed the <code>pam-u2f</code> package on</p><p>and generated U2F logins.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>sudo pamu2fcfg -uroot >> /etc/u2f_mappings</span></span></code></pre><p>I ran it once for each key, then opened the file and edit it so it looked like</p><p>this.</p><h3>Before</h3><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>root:key1root:key2</span></span></code></pre><h3>After</h3><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>root:key1:key2</span></span>
<span class="line"><span>codebam:key1:key2</span></span></code></pre><p>In <code>/etc/pam.d/system-auth</code> I added a line to the very top above all the other</p><p><code>auth</code>.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>auth	    required              pam_u2f.so authfile=/etc/u2f_mappings</span></span></code></pre><p>I added the same line to <code>/etc/pam.d/su</code> as well so that my user and root</p><p>requires a yubikey to <code>su</code>.</p><p>This makes it so that <code>sudo</code>, <code>su</code>, and logging in through a <code>getty</code> (not in</p><p>GDM for some reason, but I haven&#x27;t fixed it) require you to tap the yubikey</p><p>before typing the password. Two factors of authentication for my computer, and</p><p>every online service that supports it, backed by hardware.</p><p>Once I was done all this I committed my changes to my dotfiles pushed them to</p><p>git so that I could easily set the same thing up on my desktop which is also</p><p>running Fedora.</p>]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://seanbehan.ca/posts/websockets-rust</guid>
		<title><![CDATA[WebSockets in Rust]]></title>
		<description><![CDATA[Part one of a Rust series: a WebSocket server built on the ws crate.]]></description>
		<link>https://seanbehan.ca/posts/websockets-rust</link>
		<pubDate>Wed, 27 Nov 2019 02:56:22 GMT</pubDate>
		<category>programming</category><category>rust</category><category>websocket</category>
		<content:encoded><![CDATA[<h3>Introduction</h3><p>Today I&#x27;ll show you how you can write incredibly fast code in Rust. This is</p><p>part one of a tutorial series.</p><p>This is an intermediate tutorial, if you are unfamiliar with Rust basics I</p><p>would suggest reading the Rust book for free at <a href="https://doc.rust-lang.org/stable/book/">https://doc.rust-lang.org/stable/book/</a>.</p><h3>Project Setup</h3><p>Lets dive right in.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">cargo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> new</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rust-tutorial</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rust-tutorial</span></span></code></pre><p>Let&#x27;s build our websocket server.</p><p>Edit your <code>Cargo.toml</code> and add <a href="https://docs.rs/ws/">`ws`</a> to your</p><p><code>[dependencies]</code>.</p><p>Your <code>Cargo.toml</code> should now look something like this.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"rust-tutorial"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">version = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0.1.0"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">authors = [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Sean Behan &#x3C;codebam@riseup.net>"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">edition = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"2018"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dependencies</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ws = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"*"</span></span></code></pre><h3>Building the WebSocket Server</h3><p>Import our libraries.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">use</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ws</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">listen;</span></span></code></pre><p>Start a websocket listener.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"127.0.0.1:5000"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">out</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        move</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">msg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ws</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // handle the msg here</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre><h3>Handling Messages</h3><p>We&#x27;ll respond to recieved messages and just echo them back for now.</p><p>Of course you could easily pass this data into any function and use it to</p><p>generate a response. In fact the incoming and outgoing data doesn&#x27;t even have</p><p>to be text.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">out</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">format!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"recieved message: {}"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, msg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">into_text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">unwrap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">unwrap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">out</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">close</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ws</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">CloseCode</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Normal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre><h3>Testing the Server</h3><p>If we connect to this now using websocat, which can be installed with `cargo</p><p>install websocat`, we can see the server echos back messages that we send to</p><p>it.</p><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">$</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> websocat</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ws://127.0.0.1:5000</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hello</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> world</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">recieved</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> message:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> hello</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> world</span></span></code></pre><p>This isn&#x27;t that interesting. It&#x27;s cool that we can do all this in just 8 lines</p><p>of Rust though!</p><h3>Next Steps</h3><p>In the next part of this series I&#x27;ll show you how to use use</p><p><a href="https://docs.rs/tokio/">`tokio`</a> to create an asyncronous server so we can</p><p>manage simultaneous connections. Stay tuned!</p>]]></content:encoded>
	</item>
	</channel>
</rss>