<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://joethor.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://joethor.dev/" rel="alternate" type="text/html" /><updated>2026-06-10T11:47:34+00:00</updated><id>https://joethor.dev/feed.xml</id><title type="html">joethor.dev</title><subtitle>Personal Blog</subtitle><entry><title type="html">Self-Hosting Audiobooks</title><link href="https://joethor.dev/2026/06/09/self-hosting-audiobooks.html" rel="alternate" type="text/html" title="Self-Hosting Audiobooks" /><published>2026-06-09T00:00:00+00:00</published><updated>2026-06-09T00:00:00+00:00</updated><id>https://joethor.dev/2026/06/09/self-hosting-audiobooks</id><content type="html" xml:base="https://joethor.dev/2026/06/09/self-hosting-audiobooks.html"><![CDATA[<h1 id="audiobooks">Audiobooks</h1>

<p>I enjoy listening to audiobooks. It allows me to engage with a book while I do something else. Specifically, I enjoy listening while cleaning, running, or on long car rides. I do not like having all of the books I have purchased locked behind licenses. Here is my current set up for self-hosting audiobooks.</p>

<h2 id="libation">Libation</h2>
<h3 id="getting-your-titles">Getting Your Titles</h3>
<p>I found <a href="https://github.com/rmcrackan/Libation">Libation</a> a while back. It is, “a free, open-source application for downloading and managing your Audible audiobooks. It decrypts your library, removes DRM, and lets you own your audiobooks forever.”</p>

<p>It was shockingly easy to set up. I initially installed it on my Ubuntu laptop and when I add a title, I run the importer. It then delivers a DRM free version of my title</p>

<h2 id="audiobookshelf">Audiobookshelf</h2>
<h3 id="hosting-your-titles">Hosting Your Titles</h3>

<p><a href="https://github.com/advplyr/audiobookshelf">Audiobookshelf</a> is pretty much Plex or Jellyfin but for audiobooks and podcasts.</p>

<p>They have a docker container, which I am running on my Raspberry Pi 4 for the server.</p>

<p>It was very straightforward to set up.</p>

<h3 id="listening-to-your-titles">Listening To Your Titles</h3>
<p>They also have an incredibly performant <a href="https://play.google.com/store/apps/details?id=com.audiobookshelf.app&amp;hl=en-US&amp;pli=1">Android App</a> (especially compared to Audible’s).</p>

<p>I was able to point to my Tailscale address for the audiobookshelf docker container from the app’s interface. Now I have all my audiobooks on the go without any pain. It saves my spot, has great metadata, and has features I have not even played around with.</p>

<p>Additionally, I set it up to capture podcasts which I want to have a personal archive of from the podcatcher interface.</p>

<h2 id="do-not-sleep-on-the-library">Do Not Sleep on the Library</h2>
<h3 id="libby">Libby</h3>

<p>Usually I do not re-listen or re-read books. There is just too much out there to experience. That is why I love the library. I use <a href="https://libbyapp.com/interview/welcome#do-you-have-a-card">Libby</a> which allows you to check out audiobooks using your local library. Unless a book is an exclusive on Audible or another service, or the library in question does not have the title, Libby is my first stop. It is a phenomenal service. Libraries are amazing. And you do not need to own everything.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Audiobooks]]></summary></entry><entry><title type="html">Parental Leave</title><link href="https://joethor.dev/2026/05/12/parental-leave.html" rel="alternate" type="text/html" title="Parental Leave" /><published>2026-05-12T00:00:00+00:00</published><updated>2026-05-12T00:00:00+00:00</updated><id>https://joethor.dev/2026/05/12/parental-leave</id><content type="html" xml:base="https://joethor.dev/2026/05/12/parental-leave.html"><![CDATA[<h1 id="parental-leave">Parental Leave</h1>

<p>Over the next 10 weeks I am on parental leave with the latest addition to our family. I am very grateful that my employer and the state where I reside supports this bonding. Everyone has been relatively healthy and happy.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Parental Leave]]></summary></entry><entry><title type="html">ICE and Community</title><link href="https://joethor.dev/2026/01/24/ICE-and-community.html" rel="alternate" type="text/html" title="ICE and Community" /><published>2026-01-24T00:00:00+00:00</published><updated>2026-01-24T00:00:00+00:00</updated><id>https://joethor.dev/2026/01/24/ICE-and-community</id><content type="html" xml:base="https://joethor.dev/2026/01/24/ICE-and-community.html"><![CDATA[<h1 id="ice-and-community">ICE and Community</h1>

<p>I live in Minneapolis.</p>

<p>Renee Good was shot and killed by an ICE officer in our city.</p>

<p>My community, my neighbors, and my family will not be intimidated.</p>

<p>We are stronger because of the presence of an overpowered and cruel federally funded force.</p>

<p>We are communicating securely to track ICE activity near our schools, daycares, and homes.</p>

<p>We are decentralized and hyper local.</p>

<p>This is a war of attrition and we live here. This is a marathon, not a sprint.</p>

<p>We are domestic terrorists because we refuse to stand idly by while our neighbors and friends are abducted and beaten by undertrained bullies hiding behind masks.</p>

<p>We will continue to protest, we will continue to support one another, and we will not forget. We will be peaceful in the face of their violence.</p>

<p>We have whistles, we have each other, and we know we aren’t the baddies.</p>

<p>Update:</p>

<p>I wrote this early in the morning on January 24, 2026.</p>

<p>Since then, ICE has shot and killed another of citizen of Minneapolis.</p>

<p>This is our home. These are our friends. ICE needs to get out.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[ICE and Community]]></summary></entry><entry><title type="html">Controller Firmware Support: SteamOS vs Windows</title><link href="https://joethor.dev/2026/01/06/controller-firmware-support.html" rel="alternate" type="text/html" title="Controller Firmware Support: SteamOS vs Windows" /><published>2026-01-06T00:00:00+00:00</published><updated>2026-01-06T00:00:00+00:00</updated><id>https://joethor.dev/2026/01/06/controller-firmware-support</id><content type="html" xml:base="https://joethor.dev/2026/01/06/controller-firmware-support.html"><![CDATA[<h1 id="controller-firmware-support-steamos-vs-windows">Controller Firmware Support: SteamOS vs Windows</h1>

<p>I had two very different experiences using two different controllers.</p>

<h2 id="tldr">TL;DR</h2>
<p><a href="https://store.steampowered.com/steamos">SteamOS</a> and all the hardware that Valve has created makes a great ecosystem for gaming. You can bring what you want and it either works right away or is easy enough to get up and running.</p>

<p>Windows is becoming more of a walled garden that is unpleasant and exclusive.</p>

<h2 id="steam-hardware-personal-history">Steam Hardware Personal History</h2>

<p>Let’s talk about the Steam Controller.</p>

<p>No, not that <a href="https://store.steampowered.com/sale/steamcontroller">Steam Controller</a>.</p>

<p>This <a href="https://en.wikipedia.org/wiki/Steam_Controller">Steam Controller (2015)</a>.</p>

<p>I am a sucker bespoke hardware.</p>

<p>I was excited when Valve was making moves into the hardware space in the mid 2010s. The OG Steam Machines, Steam Link, and Steam Controller.</p>

<p>But I was not excited enough to actually buy of those things when they came out.  The latter two pieces of hardware were eventually discounted heavily and I snagged them (and they became mothballed by Valve).</p>

<p>I purchased the Steam Link in 2017 for $15.00.</p>

<p>I used intermittently when I built my gaming pc back in 2015/2016 to stream down to the TV. It worked great and actually installed the <a href="https://help.steampowered.com/en/faqs/view/6424-467A-31D9-C6CB">Steam Link app on a Raspberry Pi too</a>. Valve released the software and now you can just use it for streaming.</p>

<p>Then I purchased the Steam Controller in 2019 for $5 (plus around $10 for shipping).</p>

<p>Honestly, the Steam Controller (2015) is not great. It is not very comfortable and it feels weird overall. The track pad is not the refined track pad of the Steam Deck.</p>

<p>Honestly, after my first couple of times using it, I tossed it into the big bin of random hardware.</p>

<p>I later became a <a href="https://joethor.dev/2024/12/30/gaming-devices.html">huge fan of the Steam Deck</a>, picking one up in April of 2022.</p>

<h2 id="the-new-use-case">The New Use Case</h2>

<p>My partner and I have been playing some mystery games, specifically <a href="https://store.steampowered.com/app/653530/Return_of_the_Obra_Dinn/">Return of the Obra Dinn</a> and <a href="https://store.steampowered.com/app/1677770/The_Case_of_the_Golden_Idol/">Case of the Golden Idol</a>. These games are amazing couch coop games. One person needs to “drive” the game while also both of you discuss theories of the different mysteries on screen.</p>

<p>The set up is to plug my Steam Deck into the TV and play sitting next to each other on the couch.</p>

<p>I have a <a href="https://jsaux.com/products/steam-deck-docking-station">JSAUX Steam Deck Dock</a> plugged into our TV. This dock works pretty well! It is nice to be able to set my Steam Deck down in a specific place while it charges and the dock does a fine job with the TV display.</p>

<p>Despite having a bunch of Xbox controllers laying around, they have not been helpful with couch mystery coop.</p>

<p>We have exclusively been using the 2015 Steam Deck Controller (and even using some of its gyro controls).</p>

<p>It just worked right away when I plugged in the USB dongle into the dock.</p>

<p>The Xbox One controller has been a nightmare.</p>

<h2 id="xbox-hardware-insanity">Xbox Hardware Insanity</h2>

<p>I tried to get my Xbox One controller working on my Steam Deck via bluetooth.</p>

<p>It connected but then disconnected immediately, ping-ponging back and forth. <a href="https://leveluptalk.com/news/xbox-controller-steam-deck-connection-issues/">This is a known issue with a resolution</a>. I needed to update the firmware on the controller.</p>

<p>Apparently to update this controller, I need to use the <a href="https://apps.microsoft.com/detail/9nblggh30xj3?hl=en-US&amp;gl=US">Xbox Accessories App on Windows</a>.</p>

<p>I built a gaming PC years ago and it is the only Windows device I own still. It is in storage. I went ahead and dusted it off and booted it up.</p>

<p>I installed the Xbox Accessories App.</p>

<p>However, I am still running Windows 10 (shame on me, but I never boot the thing up).</p>

<p>The Xbox Accessories App does not work on Windows 10 any longer, it only works on Windows 11.</p>

<p>I tried to find the firmware package to do the install somehow myself, but I could not figure out a work around.</p>

<p>So to update the firmware on my Xbox One controller, I need to update to Windows 11 in order to run the firmware update.</p>

<p>But I don’t want to update my Windows gaming PC (even if the upgrade from Windows 10 to 11 seems to be free). Time is too precious to babysit a Windows update just to use a controller.</p>

<p>Compare this to the plug and play experience I had with the Steam Deck Controller from 2015. And Valve’s continued open source software support.</p>

<p>I am less and less inclined to invest in these walled gardens, like Microsoft and Apple. If I own the hardware, I should be able to use it and maintain myself with the software I want on it (or firmware). I am wary of what Valve could become, but they keep releasing things the right way.</p>

<p>I’m not sure how cogent of a thought that is, but I know I am frustrated and just want to play my mysteries.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Controller Firmware Support: SteamOS vs Windows]]></summary></entry><entry><title type="html">Gratitude Journal Google App Script Guide</title><link href="https://joethor.dev/2026/01/04/gratitude-journal-google-app-script-guide.html" rel="alternate" type="text/html" title="Gratitude Journal Google App Script Guide" /><published>2026-01-04T00:00:00+00:00</published><updated>2026-01-04T00:00:00+00:00</updated><id>https://joethor.dev/2026/01/04/gratitude-journal-google-app-script-guide</id><content type="html" xml:base="https://joethor.dev/2026/01/04/gratitude-journal-google-app-script-guide.html"><![CDATA[<h1 id="gratitude-journal-google-app-script-guide">Gratitude Journal Google App Script Guide</h1>

<p>During the pandemic I took a Coursera course <a href="https://www.coursera.org/learn/the-science-of-well-being">The Science of Well-Being</a>. As part of the course, I learned about the importance of gratitude.</p>

<p>I created a tiny script to help me write a gratitude journal each day. With the New Year, I thought it would be good to get out there and show off this script that, honestly, has helped me be more grateful.</p>

<p>It uses <a href="https://developers.google.com/workspace/guides/get-started">Google App Script</a>, which is basically Javascript that has Google Office Suite API endpoints baked in.</p>

<p>This should be something you can copy and paste, make a tiny edit to the code, and get deployed in around 5-10 minutes without any prior knowledge regarding how to code.</p>

<p>Feel free to use this script in your life to help capture your moments of gratitude and create a sustainable and positive habit.</p>

<p>I have been using this daily since March 11th, 2021. It has captured everything from the feeling of having fresh sheets on my bed to additions to our family.</p>

<h2 id="overview">Overview</h2>

<p>Each day at around 6:00 PM, a document is created in my Gratitude Journal Google Drive folder. I get sent an email with a link to the document.</p>

<p>I then fill out the journal, usually as I am going to bed, to capture what I was grateful for on the day.</p>

<h2 id="setup">Setup</h2>

<h3 id="create-google-drive-structure-and-document">Create Google Drive Structure and Document</h3>

<p>Create a new Google Doc with the name, “Gratitude Template”.</p>

<p>Here is the text I use in the template for a prompt (I found it online years ago):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Instructions

Take 5-10 minutes to write down five things for which you are grateful.

Take a moment to be mindful of the things you are writing down (e.g., imagine the person or thing you’re writing about).

Some samples:

What’s something that you are grateful to have today that you didn’t have a year ago?
What’s something or someone that makes you feel safe?
What did you accomplish today?
How are you able to help others?
What’s something or someone that makes you feel safe?


Response


</code></pre></div></div>

<p>Create a new Google Drive folder called, “Gratitude Journal”.</p>

<h4 id="a-note-on-names">A Note on Names</h4>

<p>The above names are essential for the script to work. Feel free to update them as you see fit, but you will also need to update the references in the script.</p>

<h3 id="google-app-script">Google App Script</h3>

<p>Navigate to <a href="https://script.google.com/home?pli=1">script.google.com</a></p>

<p>Create a new project.</p>

<p>Feel free to copy and past the below code.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function triggerSetUp() {

ScriptApp.triggerSetup('createNewJournal')
.timeBased()
.atHour(18)
.everyDays(1)
.nearMinute()
.create()

}
function createNewJournal(){


  var gratitudeFolder = DriveApp.getFoldersByName("Gratitude Journal").next()

  var template = gratitudeFolder.getFilesByName('Gratitude Template').next()

  var formattedDate = Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd")

  var name = 'Gratitude ' + formattedDate

  var newTrackingDoc = template.makeCopy(name, gratitudeFolder)

  emailTrackingLink(newTrackingDoc, formattedDate)
}



function emailTrackingLink(newLink, formattedDate) {
    // email link to newly created document to myself

    var htmlText = "&lt;p&gt;Remember to do your gratitude journal for " + formattedDate + ".  &lt;a href='" + newLink.getUrl() + "'&gt;Here is the direct link&lt;/a&gt;. "

    MailApp.sendEmail({
        to: 'YOUREMAIL@EMAILCOM',
        subject: "Gratitude Journal " + f.ormattedDate,
        htmlBody: htmlText

    })


};
</code></pre></div></div>

<p>Make sure you update your email in the <code class="language-plaintext highlighter-rouge">to: 'YOUREMAIL@EMAILCOM'</code> line.</p>

<h3 id="setting-up-the-trigger">Setting Up The Trigger</h3>

<p>I believe the UI on the Google App Script will give you an option on the interface for a “function to run”. It may default to <code class="language-plaintext highlighter-rouge">triggerSetUp</code>.</p>

<p>Either way, select the <code class="language-plaintext highlighter-rouge">triggerSetUp</code> from the UI toolbar and that will create the scheduled job (effectively a cron job).</p>

<p>Then, around 1800 AKA 6:00 PM (give or take 10 minutes) you should receive an email to your account from your Google account with the link to your new Gratitude Journal for the day.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Gratitude Journal Google App Script Guide]]></summary></entry><entry><title type="html">Media I Enjoyed - 2025</title><link href="https://joethor.dev/2025/12/31/media-I-enjoyed.html" rel="alternate" type="text/html" title="Media I Enjoyed - 2025" /><published>2025-12-31T00:00:00+00:00</published><updated>2025-12-31T00:00:00+00:00</updated><id>https://joethor.dev/2025/12/31/media-I-enjoyed</id><content type="html" xml:base="https://joethor.dev/2025/12/31/media-I-enjoyed.html"><![CDATA[<h1 id="media-i-enjoyed">Media I Enjoyed</h1>
<p>Similar to my <a href="https://joethor.dev/2025/12/24/goals-achieved-2025-edition.html">Goals Achieved</a> post, I want to try to capture media I enjoyed each year. I do not want to use Letterbox (in some small part because I don’t really watch movies anymore) or Backloggd. I want to own these lists.</p>

<p>I realize I did not watch many movies besides what the kids wanted to watch, so I am skipping that category.</p>

<p>A majority of this media will not be from 2025, but is media I consumed in this year.</p>

<p>I am writing this New Year’s Eve and currently have COVID-19, so the goal is to get this out the door this year.</p>

<p>I will limit each category to five pieces of media in no particular order.</p>

<p>I did have a draft which I went into details about <em>why</em> I enjoyed each piece of media. I want to do that in the future.</p>

<h2 id="video-games">Video Games</h2>

<ul>
  <li>
    <p><a href="https://store.steampowered.com/app/1030300/Hollow_Knight_Silksong/">Hollow Knight: Silksong</a></p>
  </li>
  <li>
    <p><a href="https://store.steampowered.com/app/1677770/The_Case_of_the_Golden_Idol/">Case of the Golden Idol</a></p>
  </li>
  <li>
    <p><a href="https://store.steampowered.com/app/653530/Return_of_the_Obra_Dinn/">Return of the Obra Dinn</a></p>
  </li>
  <li>
    <p><a href="https://en.wikipedia.org/wiki/Attack_of_the_Friday_Monsters!_A_Tokyo_Tale">Attack of the Friday Monsters</a></p>
  </li>
  <li>
    <p><a href="https://en.wikipedia.org/wiki/Mother_3">Mother 3</a></p>
  </li>
</ul>

<h2 id="booksaudiobooks">Books/Audiobooks</h2>

<ul>
  <li>
    <p><a href="https://app.thestorygraph.com/books/7149d003-daa2-4d4b-a83c-da82a24c1248">Enshittification</a> by Cory Doctorow</p>
  </li>
  <li>
    <p><a href="https://app.thestorygraph.com/books/32a3186e-85d1-49ce-bf80-47f126b64bbd">Rose/House</a> by Arkady Martine</p>
  </li>
  <li>
    <p><a href="https://app.thestorygraph.com/books/08ad747a-2cc1-49ad-85ae-bac1027f0ab2">Piranesi</a> by Susanna Clarke</p>
  </li>
  <li>
    <p><a href="https://app.thestorygraph.com/books/9290aba8-f93d-4e89-b7c7-ea762f718000">Dungeon Crawler Carl</a> by Matt Dinniman</p>
  </li>
  <li>
    <p><a href="https://app.thestorygraph.com/books/7112fac3-ec7f-4ead-8bc7-7b0d3a990da4">Project Hail Mary</a> by Andy Weir</p>
  </li>
</ul>

<h2 id="music">Music</h2>

<ul>
  <li>
    <p><a href="https://celeste.ink/wiki/Celeste_OST">Celeste OST</a> by Lena Raine</p>
  </li>
  <li>
    <p><a href="https://bonobomusic.bandcamp.com/album/fragments">Fragments</a> by Bonobo</p>
  </li>
  <li>
    <p><a href="https://bonobomusic.bandcamp.com/album/fragments">Good For You</a> - Aminé</p>
  </li>
</ul>

<h2 id="tv">TV</h2>

<ul>
  <li><a href="https://www.crunchyroll.com/series/GG5H5XQ0D/dan-da-dan">Dan Da Dan</a></li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[Media I Enjoyed Similar to my Goals Achieved post, I want to try to capture media I enjoyed each year. I do not want to use Letterbox (in some small part because I don’t really watch movies anymore) or Backloggd. I want to own these lists.]]></summary></entry><entry><title type="html">Goals Achieved 2025 Edition</title><link href="https://joethor.dev/2025/12/24/goals-achieved-2025-edition.html" rel="alternate" type="text/html" title="Goals Achieved 2025 Edition" /><published>2025-12-24T00:00:00+00:00</published><updated>2025-12-24T00:00:00+00:00</updated><id>https://joethor.dev/2025/12/24/goals-achieved-2025-edition</id><content type="html" xml:base="https://joethor.dev/2025/12/24/goals-achieved-2025-edition.html"><![CDATA[<h1 id="goals">Goals</h1>
<h2 id="previous-years-posts">Previous Year’s Posts</h2>
<ul>
  <li><a href="https://joethor.dev/2024/12/20/goals-achieved-2024-edition.html">2024</a></li>
</ul>

<h2 id="goals-achieved-2025---updated">Goals Achieved 2025 - Updated</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Run 600 miles
    <ul>
      <li>I hit 406.3 miles. There was a stretch in summer that things just fell off due to travel, visitors, and needing to take care of my family. Last year I hit 500.</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Optimize retirement accounts for roughly 20-25% of income
    <ul>
      <li>25% of income is going to retirement accounts</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Optimize retirement accounts pre and post tax impact
    <ul>
      <li>I think I am doing all I can do, but I will need look again for 2026.</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Run a 1/2 marathon length run
    <ul>
      <li>Achieved on 2025-06-07! Took 2:01:05 and it was lovely. I ran around the Chain of Lakes in Minneapolis.</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Learn a practical hobby (sewing, woodworking, etc)
    <ul>
      <li>I started using <a href="https://joethor.dev/2025/11/07/lost-lube-saga.html">LubeLogger</a> and I replaced the battery in the car myself.</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Build a bigger community in my neighborhood (hard to prove)
    <ul>
      <li>This has been a tough year for some communities. I have tried to donate my time and energy to helping those impacted. Keeping this a bit vague.</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" checked="checked" />Read 30 books
    <ul>
      <li><a href="https://libbyapp.com/">Libby</a> rules! And so does <a href="https://www.audiobookshelf.org/">AudioBookShelf</a>.</li>
    </ul>
  </li>
</ul>

<h2 id="rewards-2025">Rewards 2025</h2>
<p>I rewarded myself by purchasing some <a href="https://shokz.com/pages/openfit2">expensive running headphones</a>. And I have yet to use them on a run.</p>

<p>I did not do as good of a job of rewarding myself materially, but the family and I took multiple trips that were really restorative and made some great memories.</p>
<h2 id="goals-2026">Goals 2026</h2>

<p>Next year, here are the goals I have. Notably, we are expecting our third child early in the year. Last time we had a new addition to the family, I stopped running. So, my running goals have shifted to be more of “maintenance”.</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Run 400 miles</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Start lifting some weights</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Run 10 miles three times</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Be more present for my kids (hard to prove)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Continue to allocate 25% to retirement goals</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Re-optimize retirement accounts pre and post tax impact</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Set up a sustainable backup plan because of <a href="https://joethor.dev/2025/10/19/sd-card-died.html">what happened this year</a></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Read 30 books</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />12 blog posts</li>
</ul>

<h2 id="potential-goals-2026">Potential Goals 2026</h2>

<p>I am currently on the fence about these, but I want to document them for next year.</p>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Learn basic conversational Spanish. I would like to have a conversation that goes beyond, “Hi, how are you. It’s cold”.</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Learn a new coding language / framework / library
    <ul>
      <li>TypeScript</li>
      <li>Vue</li>
    </ul>
  </li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Use Ruby on Rails to publish an app in Apple App Store or Google Play.</li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[Goals Previous Year’s Posts 2024]]></summary></entry><entry><title type="html">Restart Flag Note in New Docker Service</title><link href="https://joethor.dev/2025/12/04/restart-flag-note-in-new-docker-service.html" rel="alternate" type="text/html" title="Restart Flag Note in New Docker Service" /><published>2025-12-04T00:00:00+00:00</published><updated>2025-12-04T00:00:00+00:00</updated><id>https://joethor.dev/2025/12/04/restart-flag-note-in-new-docker-service</id><content type="html" xml:base="https://joethor.dev/2025/12/04/restart-flag-note-in-new-docker-service.html"><![CDATA[<h1 id="restart-flag-note-in-new-docker-service">Restart Flag Note in New Docker Service</h1>
<p>When trying out a new Docker container, exclude the <code class="language-plaintext highlighter-rouge">restart: unless stopped</code> flag. Or any restart flag really.</p>

<p>You will thank me! This is a post for me to reference in the future but maybe you will learn something too.</p>

<p>This is especially important if you are running things on a lower powered device, such as a Raspberry Pi.</p>

<h2 id="the-specifics">The Specifics</h2>

<p>I tried out <a href="https://github.com/Stirling-Tools/Stirling-PDF">Stirling PDF</a>, a service I hope to use in my homelab.</p>

<p>PDFs are terrible in general and the software built up around them is aggressively commercialized (<em>ahem</em> Adobe).</p>

<p>I am more comfortable using docker-compose files over just using a docker command.</p>

<p>In my experience, it is easier to replicate things by capturing the configs in the yml file instead of them being lost in the terminal history.</p>

<p>I converted the docker command into a docker-compose file and ran it.</p>

<p>The Pi I was using (along with the other containers) couldn’t run it due to the required overhead. I think the CPU was the chokepoint as Stirling PDF uses Java, but I am not positive.</p>

<p>I restarted the pi (forcibly removed the power cord, I know, bad practice).</p>

<p>And then, upon restart…it became unreachable again because the CPU hungry service kicked off again.</p>

<p>I finally had to forcibly reboot (sorry again), then ssh to the device as soon as possible, then I did <code class="language-plaintext highlighter-rouge">docker compose stop</code> from within the Stirling PDF directory to free myself from the disastrous loop.</p>

<p>This has happened to me twice, and I’m writing it down here as a reminder.</p>

<h2 id="tldr">TL;DR</h2>
<p>If you are testing out a service with docker, never add the <code class="language-plaintext highlighter-rouge">restart: unless stopped</code>.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Restart Flag Note in New Docker Service When trying out a new Docker container, exclude the restart: unless stopped flag. Or any restart flag really.]]></summary></entry><entry><title type="html">No Algorithmic Feeds</title><link href="https://joethor.dev/2025/11/18/no-algorithmic-feeds.html" rel="alternate" type="text/html" title="No Algorithmic Feeds" /><published>2025-11-18T00:00:00+00:00</published><updated>2025-11-18T00:00:00+00:00</updated><id>https://joethor.dev/2025/11/18/no-algorithmic-feeds</id><content type="html" xml:base="https://joethor.dev/2025/11/18/no-algorithmic-feeds.html"><![CDATA[<h1 id="no-algorithmic-feeds">No Algorithmic Feeds</h1>

<p>This is not an original idea, it is something that came across some podcasts I listen to and my RSS Feeds.</p>

<p><a href="https://pod.link/wavelengths/episode/ZDMxYjZjZjAtMzlmNS00Y2ViLWJmMmEtODQ1NDFkMjM2MWRh">The Wavelengths podcast discussed it in the episode with Stephen Hilger</a>.</p>

<p>Additionally, <a href="https://www.theverge.com/tech/816650/ikea-just-took-over-your-smart-home">The Verge’s Installer newsletter issue 105</a> interviewed Ryder Carrol, who created the <a href="https://bulletjournal.com/pages/book">Bullet Journal Method</a>.</p>

<p>In that newsletter Carrol gives the following advice:</p>

<p>“Delete all algorithmic apps: I could quibble forever with the precise definition here, but I support the theory of getting rid of anything designed to keep your attention as long as possible”</p>

<h2 id="my-take-on-algorithmic-feeds">My Take On Algorithmic Feeds</h2>

<p>It is a nice heuristic and a way to simplify my digital life.</p>

<p>Clearly my attention and focus is worth something! Companies have devoted resources in order to grab it and never let go.</p>

<p>Recently I was really into Youtube Shorts because it kept drip feeding me skateboarding and Hollow Knight Silksong content.</p>

<p>Looking at my history and the time that I wasted, I disabled that feature (as much as I could, Youtube makes it very difficult to get rid of this completely).</p>

<h2 id="broader-thoughts-on-social-media">Broader Thoughts on Social Media</h2>

<p>I deleted my Facebook, Twitter, Instagram, and Reddit years ago. I have curated my own feeds and <a href="https://joethor.dev/2024/12/31/why-bother-with-rss.html">I have discussed it before</a>.</p>

<p>I am Discord and Linkedin, the former of which is a real problem some days. On the rare occasion I open Linkedin, the endless scrolling tends to get its hooks into me more than I would like to admit.</p>

<p>I limit my time on Discord by not having the app installed and using <a href="https://github.com/proginosko/LeechBlockNG/releases">LeechBlock</a> to limit my time each day.</p>

<p>That friction is important.</p>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>My attention and focus are finite resources which are valuable.</p>

<p>Algorithmic feeds are created to dominate those resources for the benefit of the companies which created them.</p>

<p>I have to imagine at some point, that attention translates into some type of cash flow for the company.</p>

<p>I want to profit off of my attention, not some giant tech company.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[No Algorithmic Feeds]]></summary></entry><entry><title type="html">Lost Lube Saga</title><link href="https://joethor.dev/2025/11/07/lost-lube-saga.html" rel="alternate" type="text/html" title="Lost Lube Saga" /><published>2025-11-07T00:00:00+00:00</published><updated>2025-11-07T00:00:00+00:00</updated><id>https://joethor.dev/2025/11/07/lost-lube-saga</id><content type="html" xml:base="https://joethor.dev/2025/11/07/lost-lube-saga.html"><![CDATA[<h1 id="lost-lube-saga">Lost Lube Saga</h1>
<p>To quote my <a href="https://joethor.dev/2025/10/19/sd-card-died.html">last post</a>, “I got very lucky, nothing on the card was important data.”</p>

<p>Yeah, about that.</p>

<h2 id="lubelogger">LubeLogger</h2>
<p>I was running a service that I have fallen in love with, <a href="https://lubelogger.com/">LubeLogger</a>, and it is NOT what you think.</p>

<p>I always found car maintenance daunting. My dad is very mechanically inclined. I am not.</p>

<p>LubeLogger created a structure around basic car care and allowed me to feel like I could track things and, more importantly, know I had a single source of truth.</p>

<p>Anecdotally, it has saved me money. We have jumped between different mechanics and so, they do not have a whole history of our car. Being able to say that the differential fluid was actually flushed at a different shop, even though the manufacturer does recommend it is serviced at the current mileage reading, felt really validating.</p>

<p>It even got me to replace the battery in the car myself on New Year’s Day this last year. I felt like I could actually track this stuff and, because I could track it, I could potentially do some of it.</p>

<p>Too bad I didn’t back it up.</p>

<h2 id="sd-card-died-oh-fsck">SD Card Died, Oh FSCK</h2>
<p>The micro SD card that powered the Raspberry Pi that I ran LubeLogger on died. I did try to restore it and I learned about some useful tools.  My approach lasted a weekend and was very scattershot.</p>

<h3 id="tldr">TL;DR</h3>

<p>I could not restore it.</p>

<p>I did learn about <code class="language-plaintext highlighter-rouge">fsck</code>, <code class="language-plaintext highlighter-rouge">dd</code>, and <code class="language-plaintext highlighter-rouge">ddrescue</code></p>

<h3 id="the-details">The Details</h3>

<p>I removed the card and tried to mount it on my laptop, which is running Ubuntu.</p>

<p>Here is the error message that popped up.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error mounting /dev/mmcblk0p2 at /media/MYUSERNAME/rootfs: wrong fs type, bad option, bad superblock on /dev/mmcblk0p2, missing codepage or helper program, or other error
</code></pre></div></div>
<p>I did some research and found the following potential leads:</p>

<ul>
  <li>
    <p>https://unix.stackexchange.com/questions/315063/mount-wrong-fs-type-bad-option-bad-superblock</p>
  </li>
  <li>
    <p>https://unix.stackexchange.com/questions/494491/sd-card-recovery-without-data-loss-or-corruption</p>
  </li>
  <li>
    <p>https://www.cgsecurity.org/wiki/TestDisk_Step_By_Step</p>
  </li>
</ul>

<p>I tried to get more info on my situation by running <code class="language-plaintext highlighter-rouge">lsblk -f</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sdb                                                                         
├─sdb1
│    vfat   FAT32 boot  29F5-65C4                             224.3M    12% /media/jft/boot
└─sdb2
     ext4   1.0   rootfs
                        cbe4d267-24de-4402-9a4b-1413a1da5eb8     
</code></pre></div></div>

<p>I started with DDrescue.</p>

<h3 id="ddrescue">DDrescue</h3>
<p>DDRescue can copy data and create an image from that data. Pretty cool! It also have utilities built in to try to fix corrupted data.</p>
<ul>
  <li>https://www.gnu.org/software/ddrescue/</li>
  <li>https://darwinsdata.com/how-to-use-ddrescue-in-ubuntu/</li>
  <li>https://linuxconfig.org/how-to-repair-and-clone-disk-with-ddrescue</li>
</ul>

<p>I installed DDRescue  <code class="language-plaintext highlighter-rouge">sudo apt install gddrescue</code></p>

<p>The goal at this phase was to dump the contents of the microSD card into an image that I could interact with rather than having to try to get the data off of a dying/dead microSD card every time I tried to restore the data.</p>

<p>After reading the above, I tried <code class="language-plaintext highlighter-rouge">sudo ddrescue -d /dev/sdb backup.img backup.logfile</code></p>

<p>This was successful! I got an image of the corrupted card.</p>

<p>I also tried to repair the image off the micro SD card (I should have, and probably could have done this with the image I just created).</p>

<p><code class="language-plaintext highlighter-rouge">ddrescue -f -n /dev/sda /dev/sdb sda.img</code></p>

<p>The <code class="language-plaintext highlighter-rouge">-f</code> flag is for “force” which “Force overwrite of mapfile.”
The <code class="language-plaintext highlighter-rouge">-n</code> flag is for “Skip the scraping phase. Avoids spending a lot of time trying to rescue the most difficult parts of the file. “</p>

<p>I’m not sure if those are the best flags to use, but the guides I read recommended them.</p>

<p>This ran a long time. At one point, it said it was going to take at least 24 hours, but ultimately it finished in around hours.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
GNU ddrescue 1.23
Press Ctrl-C to interrupt
     ipos:   32026 MB, non-trimmed:        0 B,  current rate:  29687 kB/s
     opos:   32026 MB, non-scraped:        0 B,  average rate:   1514 kB/s
non-tried:        0 B,  bad-sector:        0 B,    error rate:       0 B/s
  rescued:   32026 MB,   bad areas:        0,        run time:  5h 52m 23s
pct rescued:  100.00%, read errors:        0,  remaining time:         n/a
                              time since last successful read:         n/a
Finished       
</code></pre></div></div>

<p>This looked promising! It said everything was rescued!</p>

<h3 id="dd">DD</h3>

<p>I wiped the old micro SD card. 
I tried to use the very powerful <code class="language-plaintext highlighter-rouge">dd</code> to reflash the image on the recently wiped card.</p>

<ul>
  <li>https://linuxconfig.org/Dd</li>
</ul>

<p>So I tried writing <code class="language-plaintext highlighter-rouge">sudo dd if=mmcblk0.img of=/dev/mmcblk0</code></p>

<p>This ran the on the old card, but resulted in the same issue. It could not mount the root file structure and the boot partition was in read only.</p>

<h3 id="new-hardware">New Hardware</h3>
<p>I then bought a new microSD card and tried the same <code class="language-plaintext highlighter-rouge">sudo dd if=mmcblk0.img of=/dev/mmcblk0</code>, with the destination being the new card.</p>

<p>The image was succesfully written, then I tried <code class="language-plaintext highlighter-rouge">fsck</code> the card to repair the corrupted partitioned.</p>

<p><code class="language-plaintext highlighter-rouge">fsck</code> is a file system check. It can also be used to repair file systems.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fsck from util-linux 2.37.2
e2fsck 1.46.5 (30-Dec-2021)
ext2fs_open2: Bad magic number in super-block
fsck.ext2: Superblock invalid, trying backup blocks...
fsck.ext2: Bad magic number in super-block while trying to open /dev/mmcblk0

The superblock could not be read or does not describe a valid ext2/ext3/ext4
filesystem.  If the device is valid and it really contains an ext2/ext3/ext4
filesystem (and not swap or ufs or something else), then the superblock
is corrupt, and you might try running e2fsck with an alternate superblock:
    e2fsck -b 8193 &lt;device&gt;
 or
    e2fsck -b 32768 &lt;device&gt;

Found a dos partition table in /dev/mmcblk0

</code></pre></div></div>
<h2 id="summary">Summary</h2>

<p>So, after trying some command line incantations, buying new hardware, and lot of patience I am unable to recover any data from the card. I suspect I could pay some money to get the data back. If <strong>you</strong> have any suggestions, please reach out.</p>

<p>Most of the important data from LubeLogger was transcribed from invoices I saved elsewhere. Other than that, it was a lot of data related to gas mileage. I will lose that, but I am OK with that.</p>

<p>I need to re-enter the data I have, but that is fine.</p>

<p>Now, that I actually lost data due to carelessness, I will set up a backup plan. I think I will start with some very basic rsync commands. But I learned some cool and potentially useful commandline tools along the way.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Lost Lube Saga To quote my last post, “I got very lucky, nothing on the card was important data.”]]></summary></entry></feed>