<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jessica Doering</title>
    <description>The latest articles on DEV Community by Jessica Doering (@sizzlebop).</description>
    <link>https://dev.to/sizzlebop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2676985%2Fa1e931ca-7709-47e7-891c-b4430fddb1e1.jpeg</url>
      <title>DEV Community: Jessica Doering</title>
      <link>https://dev.to/sizzlebop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sizzlebop"/>
    <language>en</language>
    <item>
      <title>Finding bandwidth hogs on Linux</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 25 Jul 2026 22:35:51 +0000</pubDate>
      <link>https://dev.to/sizzlebop/finding-bandwidth-hogs-on-linux-2c92</link>
      <guid>https://dev.to/sizzlebop/finding-bandwidth-hogs-on-linux-2c92</guid>
      <description>&lt;p&gt;When a Linux server starts feeling slow, uploads stall, or network costs climb, &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;htop&lt;/code&gt; can only tell part of the story. They show CPU and memory use, not which process is moving data.&lt;/p&gt;

&lt;p&gt;For that, start with &lt;code&gt;nethogs&lt;/code&gt;, then use &lt;code&gt;iftop&lt;/code&gt; or &lt;code&gt;ss&lt;/code&gt; to confirm what the process is connected to. If you are working in a stripped-down environment, &lt;code&gt;/proc&lt;/code&gt; is still there as a fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install the tools
&lt;/h2&gt;

&lt;p&gt;These packages are available on most Linux distributions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ubuntu or Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nethogs iftop &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# RHEL or Rocky Linux, with EPEL enabled&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nethogs iftop &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Arch Linux&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; nethogs iftop &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;net-tools&lt;/code&gt; is not needed for any command in this guide. &lt;code&gt;ip&lt;/code&gt; and &lt;code&gt;ss&lt;/code&gt; are normally provided by the already-installed &lt;code&gt;iproute2&lt;/code&gt; package. On RHEL-family systems, &lt;code&gt;nethogs&lt;/code&gt; and &lt;code&gt;iftop&lt;/code&gt; may not be enabled in the default repositories, so enable an appropriate repository first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with &lt;code&gt;nethogs&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;nethogs&lt;/code&gt; groups current network traffic by process and shows the PID when it can associate traffic with a local socket. It is the fastest way to find the program responsible for a spike.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nethogs &amp;lt;interface&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;interface&amp;gt;&lt;/code&gt; with the network interface you want to inspect, such as &lt;code&gt;eth0&lt;/code&gt;. Run &lt;code&gt;ip link show&lt;/code&gt; if you need to find its name.&lt;/p&gt;

&lt;p&gt;Use this first. Once you have the PID or process name, you can investigate the connection behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check destinations with &lt;code&gt;iftop&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;iftop&lt;/code&gt; shows bandwidth by pairs of hosts and their remote IP addresses. It does not identify the owning process, but it answers the next question: which hosts are involved?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iftop &lt;span class="nt"&gt;-i&lt;/span&gt; &amp;lt;interface&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;iftop&lt;/code&gt; beside &lt;code&gt;nethogs&lt;/code&gt; when possible. One shows the process; the other shows busy host pairs and the direction of the data flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify sockets with &lt;code&gt;ss&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When you need to confirm a process's active TCP connections, use &lt;code&gt;ss&lt;/code&gt;. It maps sockets to process names and PIDs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ss &lt;span class="nt"&gt;-tnp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-t&lt;/code&gt; shows TCP connections.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-n&lt;/code&gt; keeps addresses as raw IPs instead of looking up hostnames.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p&lt;/code&gt; includes the process name and PID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful when &lt;code&gt;nethogs&lt;/code&gt; points to a PID and you need to inspect its socket connections or send queue (&lt;code&gt;Send-Q&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;/proc&lt;/code&gt; on minimal systems
&lt;/h2&gt;

&lt;p&gt;Containers, rescue shells, and stripped-down VMs may not have the monitoring tools installed. In that case, inspect the process's open file descriptors through &lt;code&gt;/proc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /proc/&amp;lt;pid&amp;gt;/fd | &lt;span class="nb"&gt;grep &lt;/span&gt;socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Entries such as &lt;code&gt;socket:[123456]&lt;/code&gt; contain a socket inode number. Look for that inode in &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/net/tcp&lt;/code&gt; for IPv4 TCP or &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/net/tcp6&lt;/code&gt; for IPv6 TCP. Use &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/net/udp*&lt;/code&gt; for UDP sockets. These paths inspect the target process's network namespace, which matters when it runs in a container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it shows&lt;/th&gt;
&lt;th&gt;Use it for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nethogs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bandwidth per program&lt;/td&gt;
&lt;td&gt;Finding the process using traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iftop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bandwidth by host pair&lt;/td&gt;
&lt;td&gt;Identifying hosts and traffic flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tnp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Established TCP sockets&lt;/td&gt;
&lt;td&gt;Mapping connections to PIDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/proc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Socket inodes and protocol tables&lt;/td&gt;
&lt;td&gt;Diagnosing minimal systems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A practical workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;nethogs&lt;/code&gt; and identify the PID using the bandwidth.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ss -tnp&lt;/code&gt; to see that process's TCP connections; use &lt;code&gt;iftop&lt;/code&gt; to identify busy host pairs on the same interface.&lt;/li&gt;
&lt;li&gt;Confirm whether the traffic is expected before stopping anything.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Routine jobs such as &lt;code&gt;cron&lt;/code&gt; tasks and &lt;code&gt;rsync&lt;/code&gt; backups often explain a network spike. A backup that overlaps with another job can look suspicious until you match the PID to the process. Confirm the process identity before killing it.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Gem: A Desktop Scanner That Finds GitHub Repos Before They're Trending</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 25 Jul 2026 00:26:56 +0000</pubDate>
      <link>https://dev.to/sizzlebop/gem-a-desktop-scanner-that-finds-github-repos-before-theyre-trending-4a0e</link>
      <guid>https://dev.to/sizzlebop/gem-a-desktop-scanner-that-finds-github-repos-before-theyre-trending-4a0e</guid>
      <description>&lt;p&gt;Gem is a desktop app for developers tired of GitHub’s trending page only ever showing what’s already popular. Instead of scrolling through the same top-100 lists, you set up “recipes”, basically saved searches combining stars range, language, topic, recency, and excluded keywords, and Gem runs them on a rotation, surfacing recently active repos that haven’t blown up yet, or might just always remain undiscovered otherwise.&lt;/p&gt;

&lt;p&gt;Save a repo and its topics score higher next time. Skip one and they score lower. You can also just set the weights manually per topic, language, or keyword if you’d rather tune it by hand than let the app learn from your behavior. Repos flow through a simple triage pipeline too: everything lands in an Inbox, then you sort into Gems, Tools to Try, or Skipped, with a Discovery Guide of prebuilt recipes (AI/ML, self-hosted, mobile, games, etc.) for when you don’t want to write GitHub search syntax from scratch.&lt;/p&gt;

&lt;p&gt;It’s built on Tauri v2 with a Rust backend (reqwest for the GitHub API, sqlx for SQLite, tokio running the scan loop) and a React/TypeScript frontend. Everything, including your recipes and scoring weights, is in a local SQLite file. No account, no server, no syncing. If you’ve ever found a great repo by accident and wondered how many more you’ve missed, give it a try and let me know what you think.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pinkpixel-dev/gem" rel="noopener noreferrer"&gt;https://github.com/pinkpixel-dev/gem&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>software</category>
    </item>
    <item>
      <title>I Built Quota to Track my AI Tools</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Thu, 16 Jul 2026 04:19:08 +0000</pubDate>
      <link>https://dev.to/sizzlebop/i-built-quota-to-track-my-ai-tools-5bb3</link>
      <guid>https://dev.to/sizzlebop/i-built-quota-to-track-my-ai-tools-5bb3</guid>
      <description>&lt;p&gt;I wanted an easy way to track usage across multiple subscriptions and multiple accounts, so I created Quota.&lt;/p&gt;

&lt;p&gt;Quota is a desktop app and VS Code extension that gives you one place to track your AI coding-tool usage across accounts and providers. Connect each service once, then see your remaining usage, pin your most used accounts, switch between layouts, and export account summaries as JSON.&lt;/p&gt;

&lt;p&gt;The VS Code extension also adds a status-bar indicator and webview panel. In the settings you can decide which providers appear in the status bar. It’s available on both the VS Code Marketplace and Open VSX, so it can work in compatible editors like Antigravity and Kiro as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec5p3wue6hb3lsl3wqxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fec5p3wue6hb3lsl3wqxy.png" alt="VSCode Dashboard" width="799" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to share because it's been really useful for me, especially being able to easily view my usage in my VSCode status bar, and hopefully others find it useful too. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5fzs3z8cvfqojypn214.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5fzs3z8cvfqojypn214.png" alt="VSCode Status Bar" width="795" height="23"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if you use Copilot, Codex, Claude Code, Cursor, Kiro, Antigravity, or some combination, please give it a try and let me know what you think!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pinkpixel-dev/quota" rel="noopener noreferrer"&gt;Desktop App&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=pinkpixel.quota-ai-usage-tracker" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://open-vsx.org/extension/pinkpixel/quota-ai-usage-tracker" rel="noopener noreferrer"&gt;Open VSX&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>agents</category>
    </item>
    <item>
      <title>I built Stitchlet because my crochet patterns were scattered across a dozen apps and a stack of paper</title>
      <dc:creator>Jessica Doering</dc:creator>
      <pubDate>Sat, 11 Jul 2026 06:53:57 +0000</pubDate>
      <link>https://dev.to/sizzlebop/i-built-stitchlet-because-my-crochet-patterns-were-scattered-across-a-dozen-apps-and-a-stack-of-2765</link>
      <guid>https://dev.to/sizzlebop/i-built-stitchlet-because-my-crochet-patterns-were-scattered-across-a-dozen-apps-and-a-stack-of-2765</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlc5b30pf1yoky650qep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvlc5b30pf1yoky650qep.png" alt="Stitchlet Dashboard" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;Stitchlet&lt;/strong&gt;, a private, self-hosted crochet project companion for keeping patterns, photos, materials, notes, and progress tracking together in one place.&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749206%2Fscreenshot2_eq9788.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749206%2Fscreenshot2_eq9788.png" alt="Stitchlet Project Details" width="800" height="422"&gt;&lt;/a&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot3_l2helt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot3_l2helt.png" alt="Stitchlet Edit Project Page" width="800" height="422"&gt;&lt;/a&gt;
  &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot4_wwyqvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdi7ctlowx%2Fimage%2Fupload%2Fv1783749207%2Fscreenshot4_wwyqvi.png" alt="Stitchlet PDF View" width="800" height="422"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;I've been crocheting and designing patterns for years, so this project came directly from problems I've run into myself. Crochet projects have a way of scattering across printed patterns, PDF files, handwritten notes, photos, row counters, and random scraps of paper. I wanted one calm, organized place for everything.&lt;/p&gt;

&lt;p&gt;With Stitchlet, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create and organize crochet projects&lt;/li&gt;
&lt;li&gt;Upload and view pattern PDFs inside the app&lt;/li&gt;
&lt;li&gt;Save project and progress photos&lt;/li&gt;
&lt;li&gt;Track rows or rounds with multiple counters&lt;/li&gt;
&lt;li&gt;Record yarn, hook size, colors, dimensions, and other materials&lt;/li&gt;
&lt;li&gt;Add custom sections for assembly notes, substitutions, or anything else&lt;/li&gt;
&lt;li&gt;Search, sort, and filter projects by status&lt;/li&gt;
&lt;li&gt;Back up and restore the entire project library&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stitchlet runs on your own computer, NAS, or home server, so your project files and notes stay under your control instead of getting locked into another subscription service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Stitchlet is self-hosted, so there's no shared public demo instance to click into. See the video below.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/lVJtfcqPZiA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can also find setup instructions and more project details in the GitHub repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/pinkpixel-dev" rel="noopener noreferrer"&gt;
        pinkpixel-dev
      &lt;/a&gt; / &lt;a href="https://github.com/pinkpixel-dev/stitchlet" rel="noopener noreferrer"&gt;
        stitchlet
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A self-hosted crochet companion for tracking projects, stitch counters, pattern PDFs, and progress photos locally on your own hardware. 🧶
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/pinkpixel-dev/stitchlet/./public/logo.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpinkpixel-dev%2Fstitchlet%2FHEAD%2F.%2Fpublic%2Flogo.png" alt="Stitchlet logo" width="300"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Stitchlet&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Stitchlet is a self-hosted crochet project tracker. It keeps your pattern PDFs, progress photos, stitch/row counters, and material notes together in one place, on hardware you control — a mini-PC, a NAS, a home server, or just your own machine. No account, no subscription, no third-party server holding your project library.&lt;/p&gt;

&lt;p&gt;I made this because my own crochet projects were scattered across printed patterns, PDFs, photos in random folders, and notes on paper. Stitchlet is basically the tool I wanted for keeping all of that in one spot while I'm actually sitting there with yarn in my hands.&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0729b507b0a7e830a231ad2c03eb41ef91b89d7e0b41f34eb1abe154f5635580/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74315f7977346566322e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/0729b507b0a7e830a231ad2c03eb41ef91b89d7e0b41f34eb1abe154f5635580/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74315f7977346566322e706e67" alt="Stitchlet Dashboard" width="48%"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/28de8b082fef40dd2592bdb5d9b59e558aa89515ac535dffafccf6f9ee62ac42/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230362f73637265656e73686f74325f6571393738382e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/28de8b082fef40dd2592bdb5d9b59e558aa89515ac535dffafccf6f9ee62ac42/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230362f73637265656e73686f74325f6571393738382e706e67" alt="Stitchlet Project Details" width="48%"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3dae06161c464de43e4d180a9befcda933d5cb6d40ce73800c085ae6c7992743/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74335f6c3268656c742e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/3dae06161c464de43e4d180a9befcda933d5cb6d40ce73800c085ae6c7992743/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74335f6c3268656c742e706e67" alt="Stitchlet Edit Project Page" width="48%"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8298615812449f451ba33a9ebe47b7a3aa14b668cf18da92e424e7303db17ef5/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74345f7777797176692e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/8298615812449f451ba33a9ebe47b7a3aa14b668cf18da92e424e7303db17ef5/68747470733a2f2f7265732e636c6f7564696e6172792e636f6d2f64693763746c6f77782f696d6167652f75706c6f61642f76313738333734393230372f73637265656e73686f74345f7777797176692e706e67" alt="Stitchlet PDF View" width="48%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard with search, status filtering (Active, Paused, Finished, Frogged), sorting by updated date/title/status, and grid or list view&lt;/li&gt;
&lt;li&gt;Upload pattern PDFs and view them in an in-app viewer, or download them&lt;/li&gt;
&lt;li&gt;Upload a project photo, with a replace/remove option&lt;/li&gt;
&lt;li&gt;Multiple row or round counters per project, each with a name…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pinkpixel-dev/stitchlet" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Stitchlet is open source, licensed under Apache 2.0.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Stitchlet is a full-stack TypeScript application built as a single package.&lt;/p&gt;

&lt;p&gt;The frontend uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;CSS variables for light and dark themes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The backend uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hono&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;SQLite&lt;/li&gt;
&lt;li&gt;Drizzle ORM&lt;/li&gt;
&lt;li&gt;Zod for shared validation and schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The React frontend uses a Hono API that manages projects, counters, custom sections, photos, PDFs, and backup operations.&lt;/p&gt;

&lt;p&gt;Project data is stored in SQLite, while uploaded PDFs and images are stored in local folders on the host system. Files are served through application routes rather than a public static directory, which keeps them out of directory listings, though access control itself is left to the network layer (see the README for details on running this behind Tailscale or a reverse proxy).&lt;/p&gt;

&lt;p&gt;One decision that mattered a lot was keeping storage local. Stitchlet runs through Docker Compose with mounted volumes for the database, uploads, and backups, so you can update or replace the container without losing your crochet library.&lt;/p&gt;

&lt;p&gt;There's also a backup and restore system that packages the SQLite database and uploaded media into a ZIP archive. You can export that from the settings page and restore it later if you move the app to another machine.&lt;/p&gt;

&lt;p&gt;I also made Stitchlet installable as a PWA, so on a phone or tablet it feels more like a dedicated crochet companion, and it’s handy when you're using the row counters mid-project.&lt;/p&gt;

&lt;p&gt;The UI is built around how crochet projects and the things crocheters actually use. The counters have large tap targets, project details stay easy to scan, and the dashboard has search, sorting, status filters, grid and list views, and mobile-friendly layouts.&lt;/p&gt;

&lt;p&gt;For deployment, there's a Docker image and a Docker Compose setup. You can run it locally, host it on a NAS, or access it privately through something like Tailscale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;This was a good reminder that a project doesn't need a huge audience to be worth building. It just needs to solve a real problem for someone, even if that someone is just me.&lt;/p&gt;

&lt;p&gt;Because I actually crochet, I already knew which details mattered, at least to me: fast counters, easy access to PDFs, project photos, a place to list all of the materials I need, a place to write down random notes, and a way to recover everything if needed.&lt;/p&gt;

&lt;p&gt;Building around my own workflow made the technical decisions easier, too... like skipping features that sound impressive on paper but wouldn't actually get used while sitting on the couch with yarn in your hands.&lt;/p&gt;




&lt;p&gt;Since crocheting is something I do all the time and I will personally really enjoy using this app, this was a really fun challenge for me, and I'm happy to be able to share it and post my first submission!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
  </channel>
</rss>
