<?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: Mark</title>
    <description>The latest articles on DEV Community by Mark (@dsubtle1).</description>
    <link>https://dev.to/dsubtle1</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%2F4078265%2F2d3ac33a-fbda-45d2-8632-d88dcce25632.png</url>
      <title>DEV Community: Mark</title>
      <link>https://dev.to/dsubtle1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dsubtle1"/>
    <language>en</language>
    <item>
      <title>I vibe-coded a self-hosted tracker for my rowing machine, then learned the hard way what "safe to make public" actually means</title>
      <dc:creator>Mark</dc:creator>
      <pubDate>Fri, 14 Aug 2026 23:24:51 +0000</pubDate>
      <link>https://dev.to/dsubtle1/i-vibe-coded-a-self-hosted-tracker-for-my-rowing-machine-then-learned-the-hard-way-what-safe-to-21jm</link>
      <guid>https://dev.to/dsubtle1/i-vibe-coded-a-self-hosted-tracker-for-my-rowing-machine-then-learned-the-hard-way-what-safe-to-21jm</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%2Fvaenhzjtlznrs7tlq3z3.jpeg" 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%2Fvaenhzjtlznrs7tlq3z3.jpeg" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I row on a Concept2 RowErg most mornings, and the Concept2 Logbook logs a workout fine but doesn't really do anything with the data — no trends, no sense of whether today should be a hard day or an easy one. So over a couple months I built Row Tracker (&lt;a href="https://github.com/dsubtle1/row-tracker" rel="noopener noreferrer"&gt;https://github.com/dsubtle1/row-tracker&lt;/a&gt;) with a lot of AI help. I'm not a professional developer — this is my first real "vibe coded" project — so a good chunk of this build was me learning what things like Python, SQLite, and CSRF tokens even are while asking Claude to build them.&lt;/p&gt;

&lt;p&gt;It syncs from the Concept2 Logbook API, tracks personal bests, spits out a daily workout based on your training load, and has an "Insights" page that tries to spot patterns in your history.&lt;/p&gt;

&lt;p&gt;The one thing I made sure I understood: keep the AI part honest&lt;/p&gt;

&lt;p&gt;Even not fully understanding the codebase, I cared a lot about one thing: the Insights page shouldn't just be an LLM making stuff up about my training. So it's a rule-based engine — plain code, no AI — that only shows a pattern once there's actually enough data to back it up. There's an optional AI layer on top (your own API key, off by default) that just rephrases numbers the rule-based part already calculated. It's not allowed to invent a number. Even as someone still learning how all the pieces fit together, that felt like a line worth holding.&lt;/p&gt;

&lt;p&gt;Then I tried to make it "safe to share" and found out I had no idea what that meant&lt;/p&gt;

&lt;p&gt;Before making the repo public I asked for a proper security review instead of just assuming two months of AI-assisted vibes were fine. They were not, and honestly most of this was new to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Gmail app password had been sitting in .env.example for weeks. I didn't know that deleting a secret from a file doesn't remove it from git history — had to learn what rewriting git history even means, then force-push the fix.&lt;/li&gt;
&lt;li&gt;My actual .env file was getting copied straight into the Docker image because nothing told Docker not to. I didn't know .dockerignore was a thing I needed until it wasn't there.&lt;/li&gt;
&lt;li&gt;A scan turned up known CVEs sitting in the base Docker image that had just never been patched, because I didn't know that was something you had to think about.&lt;/li&gt;
&lt;li&gt;I'd grabbed a copy of Chart.js off the internet at some point with no version tracked anywhere. Now it loads from a CDN with an integrity hash, so if the file it serves ever doesn't match, it just won't run instead of silently running whatever it got.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this was stuff I knew to ask for upfront — it only surfaced because I asked for a real audit instead of assuming "it runs" meant "it's fine."&lt;/p&gt;

&lt;p&gt;Stack, for what it's worth&lt;/p&gt;

&lt;p&gt;Flask + SQLAlchemy + SQLite, one Docker container, no telemetry, no backend of mine involved anywhere — it talks to your own Concept2 account with your own credentials.&lt;/p&gt;

&lt;p&gt;Still rough, still very much a learning project, but it's been my actual daily-use app for two months now. Repo's here if you want to poke at it: &lt;a href="https://github.com/dsubtle1/row-tracker" rel="noopener noreferrer"&gt;https://github.com/dsubtle1/row-tracker&lt;/a&gt; — genuinely curious what more experienced self-hosters would flag that I haven't thought to ask about yet.&lt;/p&gt;

</description>
      <category>python</category>
      <category>selfhosted</category>
      <category>flask</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
