<?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: Paul Jennings</title>
    <description>The latest articles on DEV Community by Paul Jennings (@eyup_io).</description>
    <link>https://dev.to/eyup_io</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%2F3898519%2Fb4e74706-b059-479e-adfb-a1abc5028d9a.jpg</url>
      <title>DEV Community: Paul Jennings</title>
      <link>https://dev.to/eyup_io</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eyup_io"/>
    <language>en</language>
    <item>
      <title>Meet Zoomies: free, open-source GitHub Actions runners with a live web UI</title>
      <dc:creator>Paul Jennings</dc:creator>
      <pubDate>Fri, 11 Sep 2026 06:38:53 +0000</pubDate>
      <link>https://dev.to/eyup_io/meet-zoomies-free-open-sourcegithub-actions-runners-with-a-liveweb-ui-5b90</link>
      <guid>https://dev.to/eyup_io/meet-zoomies-free-open-sourcegithub-actions-runners-with-a-liveweb-ui-5b90</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%2F8ttqupknh7in2on9msyd.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%2F8ttqupknh7in2on9msyd.png" alt="Zoomies" width="800" height="800"&gt;&lt;/a&gt; Self-hosting a GitHub Actions runner starts with a fairly simple goal: run your builds on your own hardware.&lt;/p&gt;

&lt;p&gt;Then come the operational questions. Which runners are busy? Why is that job waiting? How do you add another machine? What did the previous build leave behind? And how much work will it take to move your existing repositories over?&lt;/p&gt;

&lt;p&gt;I'm building &lt;strong&gt;&lt;a href="https://zoomies.sh/" rel="noopener noreferrer"&gt;Zoomies&lt;/a&gt;&lt;/strong&gt; to make that experience easier.&lt;/p&gt;

&lt;p&gt;It's a &lt;strong&gt;free, open-source GitHub Actions runner fleet controller&lt;/strong&gt; with ephemeral runners, autoscaling across your own hosts, a live web UI, and a migration wizard that opens reviewable pull requests.&lt;/p&gt;

&lt;p&gt;It runs as a single Go binary with SQLite. You don't need Kubernetes or a separate database server.&lt;/p&gt;

&lt;p&gt;And yes, the name comes from dogs getting the zoomies. The cocker spaniel and the swishing paw print are very much part of the project. CI tools are allowed a little personality. 🐾&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fap2k2nz2xk1x652aktaa.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fap2k2nz2xk1x652aktaa.webp" alt="Zoomies Overview dashboard showing queued jobs, running jobs, runner capacity, queue waits and recent scaling decisions." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The real Zoomies interface, populated with the project's demo fleet. Screenshots throughout this article use demo data; the displayed timings are examples, not benchmarks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Give each job a fresh runner
&lt;/h2&gt;

&lt;p&gt;A long-lived runner gradually becomes a machine with history: checked-out repositories, installed tools, temporary files and whatever yesterday's workflow left behind.&lt;/p&gt;

&lt;p&gt;Zoomies defaults to one job per runner. For container-backed pools, it starts a fresh runner container and removes it after the job finishes.&lt;/p&gt;

&lt;p&gt;The controller watches GitHub's &lt;code&gt;workflow_job&lt;/code&gt; webhooks, decides when more runners are needed, and asks an agent to start them on a matching host. Polling provides a fallback if webhook delivery isn't available.&lt;/p&gt;

&lt;p&gt;You configure pools with the labels and capacity limits your workloads need. Zoomies scales the runners within those limits and the capacity of the hosts you've connected.&lt;/p&gt;

&lt;p&gt;That last distinction matters: &lt;strong&gt;Zoomies manages runner capacity on your hosts.&lt;/strong&gt; You still choose and provide the machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  A web UI for the questions you actually ask
&lt;/h2&gt;

&lt;p&gt;The web UI is a central part of Zoomies.&lt;/p&gt;

&lt;p&gt;The Overview brings together queue depth, active jobs, runner counts, startup and registration timings, pool utilisation, and recent scaling decisions. Those decisions include explanations, so you can see why the fleet changed.&lt;/p&gt;

&lt;p&gt;From there, you can inspect pools, runners, jobs and hosts, check usage, or follow the audit trail. Pages update through the controller's event stream, with light and dark themes and a command palette for getting around quickly.&lt;/p&gt;

&lt;p&gt;When a job fails, its details show the failing step and timings, with a link to the relevant output on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F88efuum5naqgz5yypp1k.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F88efuum5naqgz5yypp1k.webp" alt="Zoomies job details showing the failed build step, queue wait, duration and a link to the failed step's GitHub log." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Job details connect a failed build to its runner, pool, timings and GitHub logs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For terminal workflows, there's also a CLI and REST API. Prometheus metrics and structured logs let Zoomies fit into existing monitoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple hosts, architectures and runner images
&lt;/h2&gt;

&lt;p&gt;You can start with one machine and an embedded agent, then add remote agents as your needs grow.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Hosts&lt;/strong&gt; page shows health, available runner slots and detected backends. Its &lt;strong&gt;Add a host&lt;/strong&gt; flow generates the command to enrol another machine using a single-use join token.&lt;/p&gt;

&lt;p&gt;Agents initiate their connections to the controller, which is useful for machines behind NAT: you don't need an inbound connection to each agent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5uhis7pbibj76axa4ilf.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5uhis7pbibj76axa4ilf.webp" alt="Zoomies Hosts page showing three demo machines, their capacity, architecture, health and detected container backends." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;See which machines have room for more runners and which need attention.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The runner image catalogue includes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runner environment&lt;/th&gt;
&lt;th&gt;Architectures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu 24.04&lt;/td&gt;
&lt;td&gt;x86-64 and arm64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu 22.04&lt;/td&gt;
&lt;td&gt;x86-64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debian 12&lt;/td&gt;
&lt;td&gt;x86-64 and arm64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fedora 42&lt;/td&gt;
&lt;td&gt;x86-64 and arm64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rocky Linux 9&lt;/td&gt;
&lt;td&gt;x86-64 and arm64&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Docker and Podman backends are available, alongside a process backend for running directly on a host. The controller and agents target Linux; macOS controller builds are intended for development. Windows runners and macOS runner images aren't currently supported.&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://zoomies.sh/#what-runs-where" rel="noopener noreferrer"&gt;platform details&lt;/a&gt; and &lt;a href="https://github.com/eyupio/zoomies/blob/main/roadmap/support-and-measurement.md" rel="noopener noreferrer"&gt;support matrix&lt;/a&gt; for the distinction between available builds and runtime qualification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation starts with one command
&lt;/h2&gt;

&lt;p&gt;The installer is available at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://zoomies.sh/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It checks the host and guides you through the deployment choices, configuration and GitHub App connection. Native, Docker Compose and single-container deployment paths are available; the setup finishes in the terminal or browser depending on the path you choose.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://zoomies.sh/quickstart/" rel="noopener noreferrer"&gt;quick start&lt;/a&gt; also explains how to download and inspect the script before running it.&lt;/p&gt;

&lt;p&gt;Once you've connected GitHub and created a pool, route a job to that pool's label. For example, a pool configured with &lt;code&gt;zoomies-linux-x64&lt;/code&gt; can receive jobs using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zoomies-linux-x64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your workflow still runs through GitHub Actions. The runner environment needs the tools your steps depend on, so choose an appropriate image and try a representative build first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration is built into the product
&lt;/h2&gt;

&lt;p&gt;Getting a fleet running is only part of adoption. Moving repositories onto it is the next part.&lt;/p&gt;

&lt;p&gt;Zoomies includes a &lt;strong&gt;Migrate&lt;/strong&gt; wizard that reads the workflows your GitHub App can access, maps supported hosted-runner labels to your pools, and shows the proposed changes before opening a pull request for each selected repository.&lt;/p&gt;

&lt;p&gt;A typical change looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- runs-on: ubuntu-latest
&lt;/span&gt;&lt;span class="gi"&gt;+ runs-on: zoomies-linux-x64
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The wizard changes the selected &lt;code&gt;runs-on&lt;/code&gt; values while preserving the surrounding formatting. It reports skipped jobs and their reasons, including matrix expressions and workflows already targeting self-hosted runners.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzoyo1r7qeujfl3z8om12.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzoyo1r7qeujfl3z8om12.webp" alt="Zoomies migration review showing a runs-on diff and a warning explaining the GitHub App permissions needed to open pull requests." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Review the exact workflow change before opening a PR. This demo also shows how missing GitHub App permissions are explained.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For teams already using static self-hosted runners, there's another route: configure a Zoomies pool to match the labels those workflows already request. Where the labels and environment are compatible, workflows can move across without editing their YAML, and you can retire the old runners gradually.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://zoomies.sh/migration/" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; covers both approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free software, infrastructure you control
&lt;/h2&gt;

&lt;p&gt;Zoomies is released under &lt;strong&gt;AGPL-3.0&lt;/strong&gt;. The source is available at &lt;strong&gt;&lt;a href="https://github.com/eyupio/zoomies" rel="noopener noreferrer"&gt;eyupio/zoomies&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The software is free; you provide the compute, storage and network resources. Whether self-hosting saves money depends on your hardware, workload and operating costs.&lt;/p&gt;

&lt;p&gt;Zoomies is currently an &lt;strong&gt;early beta&lt;/strong&gt;, intended initially for workloads you trust. Its defaults include ephemeral runners, authentication, and container jobs without access to the host Docker socket. Self-hosted jobs still execute repository code, so read the &lt;a href="https://zoomies.sh/security/" rel="noopener noreferrer"&gt;security guide&lt;/a&gt; when choosing how to deploy it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give your CI the Zoomies
&lt;/h2&gt;

&lt;p&gt;If you've been looking for a way to manage self-hosted GitHub Actions runners from a usable web interface, try Zoomies with one repository and one pool.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explore the project:&lt;/strong&gt; &lt;a href="https://zoomies.sh/" rel="noopener noreferrer"&gt;zoomies.sh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get running:&lt;/strong&gt; &lt;a href="https://zoomies.sh/quickstart/" rel="noopener noreferrer"&gt;Quick start&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;See more screenshots:&lt;/strong&gt; &lt;a href="https://zoomies.sh/ui/" rel="noopener noreferrer"&gt;Web UI tour&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Star, contribute or report an issue:&lt;/strong&gt; &lt;a href="https://github.com/eyupio/zoomies" rel="noopener noreferrer"&gt;eyupio/zoomies on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd love feedback from people running CI on their own machines: &lt;strong&gt;what's the biggest headache in your current runner setup?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>githubactions</category>
      <category>devops</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Introducing SUB12.io: A Free Precision Shooting Platform for Sub-12 Air Rifle Shooters</title>
      <dc:creator>Paul Jennings</dc:creator>
      <pubDate>Sun, 26 Apr 2026 09:56:15 +0000</pubDate>
      <link>https://dev.to/eyup_io/introducing-sub12io-a-free-precision-shooting-platform-for-sub-12-air-rifle-shooters-3hkn</link>
      <guid>https://dev.to/eyup_io/introducing-sub12io-a-free-precision-shooting-platform-for-sub-12-air-rifle-shooters-3hkn</guid>
      <description>&lt;h2&gt;
  
  
  SUB12: a free precision-shooting platform I built for a niche sport I love
&lt;/h2&gt;

&lt;p&gt;I shoot sub-12 ft·lbf air rifle benchrest from Yorkshire. Twenty-five-shot cards at known distances, where the difference between a good day and a great one is often a different pellet batch, half a click of windage, or a rest that didn't quite settle.&lt;/p&gt;

&lt;p&gt;It is, more than most sports I've tried, a sport of small details — and almost none of those details get recorded properly. They end up scattered across paper cards in a kit bag, photos on a phone, half-finished spreadsheets, and WhatsApp messages with club mates. I had a perfectly good record of my last great session somewhere; I just couldn't find it. And I couldn't answer questions that should have been easy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which rifle and pellet combination is actually working best for me?&lt;/li&gt;
&lt;li&gt;Am I improving over time, or did I just have one good Saturday?&lt;/li&gt;
&lt;li&gt;Which pellet batch produced the smallest groups?&lt;/li&gt;
&lt;li&gt;How are the rest of the club doing this league round?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://sub12.io" rel="noopener noreferrer"&gt;SUB12&lt;/a&gt;&lt;/strong&gt; — a free web app for tracking score cards, managing rifles and pellets, testing pellet performance, joining clubs, and running leagues. It's live now, and I'm looking for early users and contributors.&lt;/p&gt;

&lt;h2&gt;
  
  
  What SUB12 does
&lt;/h2&gt;

&lt;p&gt;At its core, SUB12 lets you log a 25-shot score card and link it to the rifle, pellet, score, X count, target photo, league, and club it belongs to. Once that's in the system, every card becomes part of a longer story.&lt;/p&gt;

&lt;p&gt;You can track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;25-shot score cards with shot grids&lt;/li&gt;
&lt;li&gt;Personal bests and recent form&lt;/li&gt;
&lt;li&gt;X-ring counts&lt;/li&gt;
&lt;li&gt;Per-rifle performance&lt;/li&gt;
&lt;li&gt;Pellet and rifle combinations&lt;/li&gt;
&lt;li&gt;Group sizes from pellet tests&lt;/li&gt;
&lt;li&gt;Club standings and league rounds&lt;/li&gt;
&lt;li&gt;Verification, amendments, and audit trails&lt;/li&gt;
&lt;li&gt;Activity feed posts and comments&lt;/li&gt;
&lt;li&gt;Achievements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The aim isn't to digitise a paper score card. It's to make every session useful &lt;strong&gt;later&lt;/strong&gt;.&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.amazonaws.com%2Fuploads%2Farticles%2F84wz7dat203be8njbahx.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.amazonaws.com%2Fuploads%2Farticles%2F84wz7dat203be8njbahx.png" alt="SUB12 dashboard showing best score, recent form, top rifle/pellet combo, rifle performance, leagues and clubs" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Score cards with context
&lt;/h2&gt;

&lt;p&gt;A score on its own tells you the result. A score with context tells you why.&lt;/p&gt;

&lt;p&gt;Every card in SUB12 carries the rifle, the pellet, the individual shots, the X count, the target photo, and — if the card belongs to a league — the verification status and audit trail. So when you look back at a personal best, you know exactly what produced it.&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.amazonaws.com%2Fuploads%2Farticles%2Fi1jh8y66t380ydsevwun.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.amazonaws.com%2Fuploads%2Farticles%2Fi1jh8y66t380ydsevwun.png" alt="A logged score card with shot grid, equipment, league verification, and target photo" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That trust layer matters for leagues, where club organisers need to know a card is real. It also matters for the shooter who wants to understand whether a result came from form, equipment, or a particular setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gear management
&lt;/h2&gt;

&lt;p&gt;Most benchrest shooters I know own more than one rifle and test more than one pellet. SUB12 tracks both, with profiles for each rifle and a pellet library you can attach to cards and tests.&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.amazonaws.com%2Fuploads%2Farticles%2Fobltpb3p39xocmowwhkl.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.amazonaws.com%2Fuploads%2Farticles%2Fobltpb3p39xocmowwhkl.png" alt="Gear page with rifle profiles showing calibre, power, cards logged, and best score" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The point is to make the rifle/pellet question answerable from your own data, not from gut feel after a single windy afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pellet testing — the part I'm most proud of
&lt;/h2&gt;

&lt;p&gt;Pellet choice in airgun shooting tends to be heavily anecdotal. People swear by a tin, then a bad batch lands and the swearing changes character.&lt;/p&gt;

&lt;p&gt;SUB12 has a dedicated pellet testing area that records group sizes per session, ranks rifle/pellet combinations over time, and shows performance trends across batches and lots.&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.amazonaws.com%2Fuploads%2Farticles%2Fm1lgbcocizrqrwncqts6.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.amazonaws.com%2Fuploads%2Farticles%2Fm1lgbcocizrqrwncqts6.png" alt="Pellet testing dashboard with group size trends and ranked rifle/pellet combinations" width="800" height="802"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The long-term goal is to make pellet selection more empirical without making it joyless. You still shoot the groups. SUB12 just remembers them properly so that the next time you stand in front of a wall of tins, you have data to point at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clubs and leagues
&lt;/h2&gt;

&lt;p&gt;Clubs in SUB12 bring members together with leaderboards, linked leagues, a shared activity feed, and member admin. Leagues support seasons, rounds, standings, score verification, amendments, and a full audit history.&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.amazonaws.com%2Fuploads%2Farticles%2Fgezrgxv4y50mypn90ftm.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.amazonaws.com%2Fuploads%2Farticles%2Fgezrgxv4y50mypn90ftm.png" alt="Club page showing top performers, members, linked leagues, and club feed" width="800" height="824"&gt;&lt;/a&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fk278lrvl9wimpreeb51i.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.amazonaws.com%2Fuploads%2Farticles%2Fk278lrvl9wimpreeb51i.png" alt="League Page" width="800" height="853"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a social layer too — follows, posts, comments, achievements — because a club is more than a fixture list, and a lot of what keeps people in the sport happens between sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a PWA
&lt;/h2&gt;

&lt;p&gt;Shooters capture data away from a desk, often with a poor signal, almost always on a phone. SUB12 is a progressive web app: it installs on iOS, Android, and desktop without an app store, works on capture-friendly screens at a range, and updates instantly without a release cycle.&lt;/p&gt;

&lt;p&gt;For a small niche sport, the PWA route was an easy call. I wanted to ship to every device my users actually carry, without running three codebases and waiting on store reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;For anyone here for the build details rather than the rifles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React, TypeScript, Vite, Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing &amp;amp; data&lt;/strong&gt;: TanStack Router, TanStack Query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State&lt;/strong&gt;: Zustand&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Charts&lt;/strong&gt;: Recharts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Go with Chi&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: PostgreSQL, Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile&lt;/strong&gt;: Capacitor / PWA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's a deliberately boring stack, and I'm grateful for that every time I touch it. TanStack Query took most of the data-loading mental load off the table; Go and Chi keep the backend small and well-behaved; PostgreSQL does all the heavy lifting for league scoring and audit history.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm looking for
&lt;/h2&gt;

&lt;p&gt;SUB12 works, but it has the lopsided edges any v1 has — and the people best placed to help me sand them down are the people who'll actually use it.&lt;/p&gt;

&lt;p&gt;I'd particularly value feedback from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Air rifle benchrest shooters&lt;/strong&gt; — does the score card flow match how you actually log a card?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sub-12 ft·lbf rifle owners&lt;/strong&gt; — what's missing from the gear and pellet testing setup?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Club organisers&lt;/strong&gt; — do the club tools fit how your club actually runs?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;League organisers&lt;/strong&gt; — does the verification and audit flow give you what you need?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anyone using spreadsheets or paper logs today&lt;/strong&gt; — what would make you switch?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers interested in niche sports software&lt;/strong&gt; — issues, PRs, and "have you considered" emails are all welcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The kinds of feedback that'll help most: bugs, friction points, things that almost-but-don't fit your workflow, and features you'd reach for next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Free, and staying free
&lt;/h2&gt;

&lt;p&gt;SUB12 is free now, and the plan is to keep it free. No subscription, no premium tier, no paywall on core features. It's a niche-sport tool built by someone in the niche, and that's how I'd like it to stay.&lt;/p&gt;

&lt;p&gt;If any of the above resonates, take it for a spin:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://sub12.io" rel="noopener noreferrer"&gt;https://sub12.io&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And if you find anything rough, please let me know. That's the whole reason I'm posting this.&lt;/p&gt;

</description>
      <category>sub12</category>
      <category>airrifle</category>
      <category>benchrest</category>
      <category>socialmedia</category>
    </item>
  </channel>
</rss>
