<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/atom.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Thoughts on tech</title>
  <id>https://pype.dev/tech/atom.xml</id>
  <updated>2026-10-06T21:30:00Z</updated>
  <subtitle>My thoughts and streams of consciousness organized into barely coherent posts about things</subtitle>
  <link href="https://pype.dev/tech/" rel="alternate" type="text/html"></link>
  <link href="https://pype.dev/tech/atom.xml" rel="self" type="application/atom+xml"></link>
  <author>
    <name>Nic Payne</name>
  </author>
  <generator uri="https://github.com/WaylonWalker/markata-go">markata-go</generator>
  <entry>
    <title>The Faulted Disk: harbor Replacement Writeup</title>
    <id>https://pype.dev/harbor-faulted-disk-replacement/</id>
    <updated>2026-10-06T21:30:00Z</updated>
    <published>2026-10-06T21:30:00Z</published>
    <link href="https://pype.dev/harbor-faulted-disk-replacement/" rel="alternate" type="text/html"></link>
    <summary type="text">The sequel to panicking-led-to-losing-my-desktop — this time the monitoring actually caught the disk dying, and nothing was lost.</summary>
    <content type="html">&lt;p&gt;The sequel to &lt;a href=&#34;/panicking-led-to-losing-my-desktop&#34;&gt;panicking-led-to-losing-my-desktop&lt;/a&gt; — this time the monitoring actually caught the disk dying, and nothing was lost.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-happened&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;What happened&lt;/span&gt; &lt;a href=&#34;#what-happened&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;code&gt;harbor&lt;/code&gt; is my replica pool — a 10.9T mirror (2x 12TB) that receives syncoid snapshots from &lt;code&gt;tank&lt;/code&gt;. One side of the mirror, a Seagate Exos &lt;code&gt;ST12000NM0127&lt;/code&gt; (serial &lt;code&gt;ZJV4QFLB&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt;), went &lt;strong&gt;FAULTED&lt;/strong&gt; with 14 read + 22 checksum errors.&lt;/p&gt;&#xA;&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mirror-0                              DEGRADED     0     0     0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ata-ST12000VN0008-2PH103_ZTM0NFDW  ONLINE       0     0     0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ata-ST12000NM0127_ZJV4QFLB         FAULTED     14     0    22  too many errors&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The IronWolf mirror side carried the pool — &lt;code&gt;No known data errors&lt;/code&gt;. ZFS redundancy did exactly its job.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-difference-from-last-time&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The difference from last time&lt;/span&gt; &lt;a href=&#34;#the-difference-from-last-time&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Last failure: no monitoring, found out by accident months later, desktop died.&lt;/p&gt;&#xA;&lt;p&gt;This failure: SigNoz + node-exporter&amp;rsquo;s ZFS collector → &lt;code&gt;node_zfs_zpool_state{state=&amp;quot;degraded&amp;quot;}&lt;/code&gt; → alert rule → Gotify → my phone. The gotify notification fired &lt;em&gt;before&lt;/em&gt; I knew anything was wrong.&lt;/p&gt;&#xA;&lt;h2 id=&#34;diagnosis&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;Diagnosis&lt;/span&gt; &lt;a href=&#34;#diagnosis&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Before &lt;code&gt;zpool clear&lt;/code&gt; or replace — check SMART. &lt;code&gt;smartctl-exporter&lt;/code&gt; already scrapes all disks into SigNoz, so I didn&amp;rsquo;t even need sudo:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Reallocated_Sector_Ct&lt;/code&gt; raw = &lt;strong&gt;3024&lt;/strong&gt; and counting&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Offline_Uncorrectable&lt;/code&gt; value/worst still 100 but raw errors climbing&lt;/li&gt;&#xA;&lt;li&gt;SMART overall: still PASS (SMART&amp;rsquo;s overall bit is conservative until threshold)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;3k+ remapped sectors is a platter going bad — not a cable blip. Verdict: replace, don&amp;rsquo;t clear.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-swap-the-annoying-part&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The swap (the annoying part)&lt;/span&gt; &lt;a href=&#34;#the-swap-the-annoying-part&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Hot-plug is never as smooth as it should be:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;code&gt;zpool offline harbor &amp;lt;old&amp;gt;&lt;/code&gt; to stop writes to the dead drive — actually skipped; drive fell off the bus on its own&lt;/li&gt;&#xA;&lt;li&gt;New IronWolf &lt;code&gt;ZTN1CQ07&lt;/code&gt; in hand → plugged into ghost&amp;rsquo;s SATA → &lt;strong&gt;didn&amp;rsquo;t enumerate&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;Forced rescan (&lt;code&gt;echo &amp;quot;- - -&amp;quot; | sudo tee /sys/class/scsi_host/host*/scan&lt;/code&gt;) → nothing&lt;/li&gt;&#xA;&lt;li&gt;Moved it to the &lt;em&gt;old drive&amp;rsquo;s port&lt;/em&gt; → nothing&lt;/li&gt;&#xA;&lt;li&gt;Sabrent USB dock on aurora → dock enumerated as &lt;code&gt;0B&lt;/code&gt; device, no disk behind it → reseated + replugged → &lt;strong&gt;drive spun up and appeared&lt;/strong&gt;: &lt;code&gt;/dev/sdd&lt;/code&gt;, 10.9T, old ZFS partition table on it (used drive — &lt;code&gt;part1&lt;/code&gt;/&lt;code&gt;part9&lt;/code&gt; layout)&lt;/li&gt;&#xA;&lt;li&gt;Back to ghost, direct SATA → enumerated as &lt;code&gt;sdb&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Lesson: &amp;ldquo;spins up but doesn&amp;rsquo;t enumerate&amp;rdquo; on direct SATA + dock-shows-0B = seating/power problem, not DOA. The drive was fine all along.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-replace&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The replace&lt;/span&gt; &lt;a href=&#34;#the-replace&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo zpool replace harbor &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ata-ST12000NM0127_ZJV4QFLB &lt;span class=&#34;se&#34;&gt;\&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ata-ST12000VN0008-2PH103_ZTN1CQ07&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Resilver: &lt;strong&gt;8.80T in 18h38m, 0 errors&lt;/strong&gt; (~154M/s). Pool stayed online and usable the whole time.&lt;/p&gt;&#xA;&lt;p&gt;One gotcha: after resilver, &lt;code&gt;zpool status&lt;/code&gt; showed &lt;code&gt;errors: 1 data errors&lt;/code&gt; — but &lt;code&gt;zpool status -v&lt;/code&gt; showed an &lt;strong&gt;empty error list&lt;/strong&gt;. The corrupted data was already repaired; the counter was just stale. &lt;code&gt;sudo zpool clear harbor&lt;/code&gt; → clean.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-monitoring-that-made-this-possible&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The monitoring that made this possible&lt;/span&gt; &lt;a href=&#34;#the-monitoring-that-made-this-possible&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Wired during this session (all now in SigNoz):&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;node_zfs_zpool_state&lt;/code&gt; — pool health (node-exporter zfs collector)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;smartctl-exporter&lt;/code&gt; — SMART attributes incl. reallocated sectors (the early-death signal)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;zfs-metrics.sh&lt;/code&gt; textfile bridge — sanoid &lt;code&gt;--monitor-*&lt;/code&gt; exit codes, zpool error counters, scrub ages, resilver %, syncoid last-success&lt;/li&gt;&#xA;&lt;li&gt;Alert rules → Gotify → phone&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The replication alerting even proved itself live: syncoid failed twice during the resilver window and I got paged on both. Turned out to be send-time contention — self-healed once resilver finished — but the notification path works end to end.&lt;/p&gt;&#xA;&lt;h2 id=&#34;remaining-homework&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;Remaining homework&lt;/span&gt; &lt;a href=&#34;#remaining-homework&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Buy the replacement spare (the shelf&amp;rsquo;s empty now)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;10Fold&lt;/code&gt; datasets are garbage + have zero snapshots — destroy&lt;/li&gt;&#xA;&lt;li&gt;Every scheduled job emits &lt;code&gt;cron_last_success_epoch&lt;/code&gt; now — if a job dies silently again, I&amp;rsquo;ll know&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Same failure as April, opposite outcome. Redundancy did the protection; monitoring did the &lt;em&gt;detection&lt;/em&gt;. You need both.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;em&gt;Co-authored with Devin, who ran the monitoring stack, the SMART diagnosis, and the alerting loop.&lt;/em&gt;&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>It Will Be Hard To Do More</title>
    <id>https://pype.dev/it-will-be-hard-to-do-more/</id>
    <updated>2026-05-22T06:23:20Z</updated>
    <published>2026-05-22T06:23:20Z</published>
    <link href="https://pype.dev/it-will-be-hard-to-do-more/" rel="alternate" type="text/html"></link>
    <summary type="text">Recently I was given a raise at work after I broached the subject with my manager. I wasn&#39;t sure what the outcome would be but I have learned two things in...</summary>
    <content type="html">&lt;p&gt;Recently I was given a raise at work after I broached the subject with my&#xA;manager. I wasn&amp;rsquo;t sure what the outcome would be but I have learned two things&#xA;in nearly a decade in corporate America:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;No one will fight for you harder than yourself&lt;/li&gt;&#xA;&lt;li&gt;The worst they can say is no&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;the-raise&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The Raise&lt;/span&gt; &lt;a href=&#34;#the-raise&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;So I talked with my manager about my workload and compensation, and to my&#xA;surprise he took me seriously. He went up the chain with some accomplishments&#xA;of the year that I wrote up for him (it&amp;rsquo;s his job to advocate for me, but it&amp;rsquo;s&#xA;certainly my job to give him the ammunition to take into battle). Before much&#xA;time had passed, a big wig put some time on my calendar. I had a good&#xA;relationship with my (then) manager, and even with the big wig, so there was&#xA;always some amount of personal connection amidst the money-talk.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-reorg&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The Reorg&lt;/span&gt; &lt;a href=&#34;#the-reorg&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Cue a managerial change and reorganization of the team. The dust settled, and&#xA;my manager wasn&amp;rsquo;t my manager and the big wig wasn&amp;rsquo;t a big wig over my team&#xA;anymore. The details aren&amp;rsquo;t that relevant, but the outcome was that my &lt;em&gt;now&lt;/em&gt;&#xA;manager is someone I don&amp;rsquo;t know personally — back to square one of&#xA;professional-relationship-building.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-conversation&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The Conversation&lt;/span&gt; &lt;a href=&#34;#the-conversation&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Due to the timing of it all, when my raise became official it was this new&#xA;manager who signed off on it. It was their happy-responsibility to have a quick&#xA;chat to tell me it was official. But that conversation was the most&#xA;awkward of the three.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Does this raise of X% close the gap with the other competing opportunities&#xA;you mentioned to (previous manager)? Because it&amp;rsquo;ll be hard to do more than&#xA;this.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;div class=&#34;admonition danger&#34;&gt;&#xA;&lt;p class=&#34;admonition-title&#34;&gt;???&lt;/p&gt;&#xA;&lt;p&gt;Hard to do more?&lt;/p&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;I was taken aback initially. It felt almost threatening.&lt;/p&gt;&#xA;&lt;p&gt;I really wasn&amp;rsquo;t sure how to respond. I think I was grateful and cordial,&#xA;talked about how I really do believe in the mission of our work and I&amp;rsquo;m&#xA;thankful that I can receive a raise. This lady is nice but she doesn&amp;rsquo;t know&#xA;what my &amp;ldquo;other opportunities&amp;rdquo; are, so there&amp;rsquo;s no honest answer for me to give.&#xA;I wanted to be sarcastic, crack a joke about record profits, make light of her&#xA;describing me as a &amp;ldquo;critical player in enterprise deliverables,&amp;rdquo; but I&amp;rsquo;m not&#xA;trying to make enemies.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-reframe&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The Reframe&lt;/span&gt; &lt;a href=&#34;#the-reframe&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;So I called a friend to vent, of course, and he gave me some good advice.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;It was a threat.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;But not in the way I took it at first. My friend has been at the same company&#xA;longer than me, and he expounded on her words: &amp;ldquo;it will be hard to do more.&amp;rdquo;&#xA;I felt like she was annoyed with me for fighting for a raise, but my friend&#xA;said she was probably just trying to give me a realistic view of my position&#xA;in the company. &amp;ldquo;It will be hard to do more&amp;rdquo; means there are processes in&#xA;place at such a large enterprise. Rewarding start-up-like behavior and&#xA;contribution just isn&amp;rsquo;t well-supported. It&amp;rsquo;s much easier to let someone quit&#xA;who&amp;rsquo;s been with the company a decade and replace them for 20% more than it is&#xA;to just pay them 20% more. If someone (like me) isn&amp;rsquo;t satisfied, that&amp;rsquo;s not the&#xA;only thing in the conversation - time at the company, time in position, time&#xA;under specific manager, etc. all are hurdles to more compensation, and I&amp;rsquo;m&#xA;referring to salary anyways, equity feels like it&amp;rsquo;s out of my grasp. But if I&amp;rsquo;m&#xA;gone and market conditions dictate that my position and responsibilities are&#xA;worth 20% more than what I was being paid, that&amp;rsquo;s what the position will be&#xA;posted at. My previous manager and the big wig were the types to say&#xA;policy-be-damned when it would get in the way - it might be how I got this&#xA;raise in the first place. But no shade to my new manager, they&amp;rsquo;re just doing their job.&lt;/p&gt;&#xA;&lt;p&gt;In the end, they were just being honest, and I should appreciate that.&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Panicking Led to Losing My Desktop</title>
    <id>https://pype.dev/panicking-led-to-losing-my-desktop/</id>
    <updated>2026-05-13T08:24:00Z</updated>
    <published>2026-05-13T08:24:00Z</published>
    <link href="https://pype.dev/panicking-led-to-losing-my-desktop/" rel="alternate" type="text/html"></link>
    <summary type="text">I thought I had backups handled... can you imagine how the rest of this post is going to go with that intro?</summary>
    <content type="text">&#xA;## False Sense of Security&#xA;&#xA;I thought I had backups handled... can you imagine how the rest of this post is&#xA;going to go with that intro?&#xA;&#xA;To be fair, I do have backups figured out on my NAS - simple ZFS +&#xA;sanoid/syncoid + replica pool + off-site backup with simple restore pathways.&#xA;However, my desktop has been another story entirely. My desktop OS didn&#39;t&#xA;support ZFS when I started checking it out, and I spent weeks thinking through&#xA;how I would backup my HOME directory and projects mostly. I landed on a&#xA;solution that I did validate once, but it fell off my radar and lo&#39; and behold&#xA;that was problematic...&#xA;&#xA;So that backup was based on restic for my home directory, but it was lazy. I&#xA;verified it one time but I had built it with ai, thought I understood the&#xA;restic repo part, and then promptly moved on with my life never buttoning it&#xA;all up. That home directory backup got too big for where I was going to end up&#xA;restoring it. My desktop system was installed on a 4 TB NVMe drive and due to&#xA;the circumstances spawning this blog post I was gonna have to drop to a 500 GB&#xA;boot drive with some extra disks as the storage layer. Overall it looked like:&#xA;&#xA;- A 4 TB SSD that was going bad - old OS&#xA;- A 500 GB SSD, that was going to be my new operating system boot disk&#xA;- A 2 TB SSD that was originally going to be this external storage volume&#xA;  anyways but I never set it up because the version of Aurora I was running&#xA;  didn&#39;t have ZFS, I was married to the idea of using ZFS, so I never ended up&#xA;  taking advantage of the space. However it was moot to me because my boot drive&#xA;  was 4 TB, high quality drive, so I was &#34;just sure&#34; I didn&#39;t need it.&#xA;- AND a 4 TB rust disk as well, which was already a ZFS pool, left over from a&#xA;  previous desktop configuration, and admittedly I had forgotten it was even in the system.&#xA;&#xA;## The Storm&#xA;&#xA;If it wasn&#39;t clear the problem is that my super-nice high-speed 4TB NVMe drive&#xA;was going bad, like really bad. Eventually my OS stopped booting, it was even&#xA;difficult to live-boot from any other ISO due to, I think ultimately, that disk&#xA;causing such extreme latency in the start-up processes that they just failed.&#xA;So I quickly found myself with little-to-no access to my primary desktop&#39;s&#xA;data...&#xA;&#xA;## Where It Went Wrong&#xA;&#xA;What I did is I live booted into an Ubuntu server environment (which took blood&#xA;sweat and tears to successfully get into), mounted my home directory from the 4 TB SSD, and&#xA;tried to continue my restic backup to my NAS, like an idiot. But at the same&#xA;time I also tried to prune it by only backing up a few projects because I&#xA;was getting worried about time. This was the first primary mistake - trying to&#xA;muck with my backup script under duress.&#xA;&#xA;Then over the course of the whole thing it ended up taking over a week to solve&#xA;this when it could&#39;ve been 2-3 days. So say it with me kids - &#34;Don&#39;t make&#xA;decisions under duress&#34;&#xA;&#xA;## Climbing Out&#xA;&#xA;I downloaded opencode and had it help me write the right excludes syntax in my&#xA;restic backup script and got it back up going. That went ok but opencode agents&#xA;had no historical context for why anything was the way it was, and frankly an&#xA;agent would&#39;ve been misled thinking the backup solution was much more solid&#xA;than it was due to how I documented it.&#xA;&#xA;Agents also miss things... in my chat sessions it knew about the other 2&#xA;available disks on the desktop system, I could have done a fresh backup to the 4 TB&#xA;spinning rust disk no problem: install zfs, mount the pool, change target of&#xA;restic, run full... that would&#39;ve been beautifully simple. But instead I&#xA;trimmed it down and backed not-everything up to the NAS over the network, and&#xA;to a different backup target nonetheless... SMH.&#xA;&#xA;As I started to consider which OS I was going to go with next I failed to&#xA;install Pop_OS! or Ubuntu onto the new disc... Then I tried Omarchy and the&#xA;install script just looped. So, I reinstalled Aurora onto the new 500 GB disk&#xA;and then quickly realized I don&#39;t have Firefox tabs, my SSH keys are in that&#xA;restic backup, my ssh config, api keys in hidden files.... Everything is in&#xA;that restic backup... The backup that&#39;s too big to restore to my new boot drive.&#xA;&#xA;But you know what I have? That 2 terabyte disk mounted just fine as a&#xA;ZFS dataset. And I could mount the 4 TB rust disk with zfs as well because this&#xA;version of Aurora has zfs working flawlessly!&#xA;&#xA;## Hindsight&#xA;&#xA;What I should&#39;ve done is so simple... While in that ubuntu live environment I&#xA;should&#39;ve just either updated restic to be a local backup to the 4 TB rust&#xA;disk, or rsync&#39;d my home directory to it plain and simple... I got all in my&#xA;head about not backing up python venvs, node_modules, etc. that I didn&#39;t think&#xA;to just basically carbon copy it all to a healthy disk and then prune it later.&#xA;Then I could&#39;ve synced everything back over that I needed to the new Desktop&#39;s&#xA;$HOME and then scheduled the rsync or restic again to that locally mounted disk.&#xA;&#xA;## The Detail I Left Out&#xA;&#xA;The keen reader might stop to think... why not just mount the old 4TB disk and&#xA;copy what you need to your new desktop? And that&#39;s a prudent question...&#xA;However, in order to get anything installed I had to physically remove the 4TB&#xA;SSD from the motherboard, which was basically a full PC tear-down. From there I&#xA;was able to at least boot in and out of iso&#39;s like you&#39;d otherwise expect, and&#xA;I have a USB/NVMe adapter so I planned to mount the old drive and copy things over from&#xA;there... But sadly... it won&#39;t mount. it&#39;s dead-dead and it appears that&#xA;anything I didn&#39;t save in my days-long-panicked-state is just. gone.&#xA;&#xA;I feel pretty stupid to have not taken advantage of the 2 available disks local&#xA;to the machine, to have naively copied stuff over and dealt with the&#xA;organization later once my OS was back up. I tried to be smart and efficient&#xA;and ended up wasting so much time and losing quite a lot of &#34;stuff&#34;... ideas,&#xA;blog posts that I never committed, etc.&#xA;&#xA;## Current Status&#xA;&#xA;So a few lessons...&#xA;&#xA;1. untested backups are not backups&#xA;2. false backups might be worse than none, although I did at least save a few things so maybe the jury is out here&#xA;3. making decisions while stressed out will lead to missing obviously better pathways... slow down, talk it out&#xA;&#xA;As for my current status - I&#39;m working on [[desktop-setup-2026]] and recovering what I can from my haphazard&#39;d rsyncs in the live ubuntu env I got into. I&#39;m also setting up a new Linux laptop at work at the same time so maybe I&#39;ll hve some workflow changes to write about in the future. For now, it&#39;s nice to be forced to accept that not every idea was that important, the good stuff will come back around, and ultimately computers and shit are just things, they&#39;re not life.&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>The Relief of Earning</title>
    <id>https://pype.dev/the-relief-of-earning/</id>
    <updated>2026-05-08T05:43:39Z</updated>
    <published>2026-05-08T05:43:39Z</published>
    <link href="https://pype.dev/the-relief-of-earning/" rel="alternate" type="text/html"></link>
    <summary type="text">Work has been crazy for months, and I haven&#39;t written too much about it, a few notes here and there but nothing substantial. The craziness is sourced in a...</summary>
    <content type="html">&lt;h2 id=&#34;the-setup&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The Setup&lt;/span&gt; &lt;a href=&#34;#the-setup&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Work has been crazy for months, and I haven&amp;rsquo;t written too much about it, a few&#xA;notes here and there but nothing substantial. The craziness is sourced in a&#xA;pretty cliched-scenario where we have lofty goals and important things to build&#xA;but the foundation on which we are building, like architectural decisions, is&#xA;basically wet sand. So as I&amp;rsquo;ve been trying to find my footing in the mud I feel&#xA;like I end up spinning my wheels moreso than making any progress. The chaos led&#xA;me eventually to approach my manager about my compensation - I make a nice&#xA;living but for the stress I&amp;rsquo;d been feeling I could certainly go make more. My&#xA;manager successfully fought for me up the chain and I was given a slightly bigger&#xA;carrot, but that&amp;rsquo;s not the point of this post. I started to feel even more&#xA;anxiety after I was told about the raise because, just like with AI and agentic&#xA;coding, now I was sure the expectations would be even higher. I didn&amp;rsquo;t think&#xA;anything would actually come from me asking my boss about a raise, but now that&#xA;it did I was afraid the ceiling of expectations was just going up up up.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-carrot-and-the-anxiety&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The Carrot and the Anxiety&lt;/span&gt; &lt;a href=&#34;#the-carrot-and-the-anxiety&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;That&amp;rsquo;s where the relief of earning comes in&amp;hellip; I mentioned this to my project&#xA;manager, and we have candid conversations regularly, and he told me something&#xA;that I think some people might have been offended by but I was genuinely&#xA;relieved to hear it. He said he&amp;rsquo;s glad I got a raise, and that he told our boss&#xA;to &lt;em&gt;not&lt;/em&gt; really pursue it for me until I had actually delivered something. So even&#xA;though I feel like I&amp;rsquo;m spinning my wheels a lot, my direct leadership sees real&#xA;value - and the relief is that I can rest in confirmation that the carrot is a&#xA;response to work I already did, not a quiet elevation of expectations.&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Fixing Firefox Launcher on Kubuntu 22</title>
    <id>https://pype.dev/fixing-firefox-launcher-on-kubuntu-22/</id>
    <updated>2026-04-11T10:32:50Z</updated>
    <published>2026-04-11T10:32:50Z</published>
    <link href="https://pype.dev/fixing-firefox-launcher-on-kubuntu-22/" rel="alternate" type="text/html"></link>
    <summary type="text">Problem: Firefox installed but missing from KDE/Plasma app menu - Root cause: No .desktop launcher file — Firefox only worked from terminal - Solution:...</summary>
    <content type="html">&lt;ul&gt;&#xA;&lt;li&gt;Problem: Firefox installed but missing from KDE/Plasma app menu&lt;/li&gt;&#xA;&lt;li&gt;Root cause: No .desktop launcher file — Firefox only worked from terminal&lt;/li&gt;&#xA;&lt;li&gt;Solution: Created desktop entry with standard KDE fields&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code&gt;&#xA;[Desktop Entry]&#xA;Version=1.0&#xA;Name=Firefox&#xA;GenericName=Web Browser&#xA;Comment=Web Browser&#xA;Exec=firefox %u&#xA;Terminal=false&#xA;Icon=firefox&#xA;Type=Application&#xA;Categories=Network;WebBrowser;&#xA;MimeType=x-scheme-handler/http;x-scheme-handler/https;&#xA;StartupNotify=true&#xA;StartupWMClass=firefox&#xA;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Location: ~/.local/share/applications/firefox.desktop (user-local, no sudo needed)&lt;/li&gt;&#xA;&lt;li&gt;Discovery step: Checked /usr/share/applications/ — empty for Firefox&lt;/li&gt;&#xA;&lt;li&gt;Applied fix: Ran kbuildsycoca5 to rebuild Plasma&amp;rsquo;s menu cache&lt;/li&gt;&#xA;&lt;li&gt;Result: Firefox now appears in app menu (Network category) and Alt+F2 autocomplete&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Data Loading is a Huge Deal</title>
    <id>https://pype.dev/data-loading-is-a-huge-deal/</id>
    <updated>2026-04-08T07:40:14Z</updated>
    <published>2026-04-08T07:40:14Z</published>
    <link href="https://pype.dev/data-loading-is-a-huge-deal/" rel="alternate" type="text/html"></link>
    <summary type="text">I&#39;ve been thinking about the work I am doing and have to do in my role at Cat, in Cat Autonomy, building Forge (see forge-ahead). I feel like I have little...</summary>
    <content type="text">&#xA;I&#39;ve been thinking about the work I am doing and have to do in my role at Cat,&#xA;in Cat Autonomy, building Forge (see [[forge-ahead]]). I feel like I have&#xA;little revelations almost every day now, not that it means I&#39;m writing&#xA;something amazing and producing it really fast but there&#39;s just a whole suite&#xA;of problems that different technologies solve at different levels and the more&#xA;I become aware of the problems that exist, the more the existence of some&#xA;solutions makes sense.&#xA;&#xA;!!! note &#34;The Problem Perspective&#34;&#xA;&#xA;    I don&#39;t know if this is a real thinking technique or if I&#39;m onto something&#xA;    novel(doubt) but I think a lot in terms of problems - &#34;what problem needs&#xA;    solving?&#34; and that&#39;s how I&#39;ve come to prioritize my work, it&#39;s only been very&#xA;    recently that I&#39;ve realized I do this and I think I should highlight it&#39;s very&#xA;    important to document the problem, otherwise every day you might try to solve a&#xA;    different problem but be working on the same code&#xA;&#xA;!!! note &#34;Problem Space&#34;&#xA;&#xA;    Kedro solves a lot of these problems, so when making rada in Reman the problem&#xA;    space was already more contained and narrow, Forge&#39;s problem-space is much more&#xA;    vast&#xA;&#xA;The one problem I&#39;m really fixated on right now is data loading, the problem I&#xA;need to solve is accessing data from a wide-variety of scripts/tools, without a&#xA;framework or standard method/library of accessing data in the first place. I&#39;ve&#xA;seen lots of projects on GitHub claim to make data loading easier and I didn&#39;t&#xA;quite understand what problem they were solving... one example to name is [Data&#xA;Load Tool](https://dlthub.com/). I&#39;ve seen similar ones I don&#39;t have the name&#xA;for right now that claim to make it easy/fast to load data from s3, or ways to&#xA;make s3 and a database both abstract in the user-experience for loading data.&#xA;But I hadn&#39;t really understood why these tools existed. I have a lot of&#xA;experience with [Kedro](https://dlthub.com/) and their&#xA;[DataCatalog](https://docs.kedro.org/en/latest/catalog-data/introduction/)&#xA;which provides a python object over a set of yamlfiles that makes it pretty&#xA;simple to load and save data in a way that isolates I/O from the business&#xA;logic. But what I didn&#39;t realize at the time how powerful that catalog was, the&#xA;power of standard patterns and shared libraries. Now that I don&#39;t have it&#xA;available to me, I&#39;m quite aware of the absence.&#xA;&#xA;In my new role something I&#39;m realizing is that for all the developers my team&#xA;now supports, there isn&#39;t a canonical way to access data. When I was in Reman&#xA;and working with kedro, the DataCatalog was the access pattern and so when I&#xA;was developing a platform I never really had to think about it - it was an&#xA;established pattern that I treated as a constraint and then built processes&#xA;around it. I&#39;ve been battling some mental block for weeks on Forge because of&#xA;the lack of that canonical pattern, and as I&#39;ve talked with other engineers it&#xA;seems like the baseline assumption is that data is just available on a&#xA;filesystem, but everyone&#39;s code loads data in different ways. On my small Reman&#xA;team, with common patterns to build on, it was easy to make things cloud-native&#xA;or shim in some devops to improve people&#39;s lives. But when everyone&#39;s doing&#xA;their own thing, and everyone&#39;s &#34;own thing&#34; is very much built-on some rigid tribal&#xA;patterns then it&#39;s hard to really move fast cause everyone isn&#39;t already moving&#xA;in the same direction.&#xA;&#xA;That made me realize that the first problem Forge needed to solve was in&#xA;providing a way for engineers to have filesystem-native data access in the&#xA;Cloud, where we are S3-first in our storage philosophy. I didn&#39;t need to figure&#xA;out a way for everyone to name a dataset, define the dataset in the first&#xA;place, and give a nice `my_dataset.load` that worked in python, bash, cpp, and&#xA;who knows what else.... I reframed the problem from &#34;how do engineers load up&#xA;the data&#34; to &#34;how do engineers have access to the data&#34;. The requirements of&#xA;the Cat Autonomy group was pretty simple: POSIX-compliant storage.&#xA;&#xA;My pathway to solving this problem is initially underway, I can&#39;t imagine it&#39;ll&#xA;be too difficult to setup for FSx instances for teams and give them an api to&#xA;run a Batch Job with the FSx mounted. From there, their code can load data from&#xA;`/mnt/fsx/&lt;whatever&gt;` just like they otherwise could be doing locally. Or maybe&#xA;FSx will let us setup mounts to very flexible mount points and their local&#xA;scripts will &#34;just work&#34; :shrugs:. I don&#39;t know the exact shape, but after&#xA;realizing the loading data is a big deal, I&#39;m thankful I have a narrower&#xA;problem to solve first.&#xA;&#xA;!!! note &#34;S3 Files&#34;&#xA;&#xA;    Literally yesterday, AWS launched &#34;S3 Files&#34; offering an NFS filesystem service over buckets. I&#39;m not sure if NFS is going to be a viable filesystem protocol for all of our use cases, but looks like we&#39;re not the only people who need the filesystem access patterns over S3.&#xA;&#xA;!!! warning &#34;A Future Problem - Canonical Reference&#34;&#xA;&#xA;    Another high-value thing Forge needs to solve is &#34;what is data&#34;. The data&#xA;    formats we have are not super simple, it&#39;s not just a set of SQL tables. We&#xA;    have files that relate to each other based on hard-filepath patterns, and those&#xA;    patterns are full of tribal knowledge and distributed processes. So a simple&#xA;    question like &#34;How do I use forge to access my data&#34; is hard. In Reman a data&#xA;    scientist would ask &#34;how do I use rada to access my data?&#34; and the answer is&#xA;    &#34;We use Kedro, and Kedro solved that problem for us via the Catalog&#34; but&#xA;    without Kedro, without 100% being in python (devs are also in embedded systems,&#xA;    cpp code, and more), without even consistent practices in the existing &#34;how do&#xA;    I access my data&#34; workflows, it&#39;s really impossible to systemetize and codify&#xA;    it. It is my next challenge to tackle though...&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Agents can miss obvious things too</title>
    <id>https://pype.dev/agents-can-miss-obvious-things-too/</id>
    <updated>2026-04-03T14:39:51Z</updated>
    <published>2026-04-03T14:39:51Z</published>
    <link href="https://pype.dev/agents-can-miss-obvious-things-too/" rel="alternate" type="text/html"></link>
    <summary type="text">I am working with MiniMax M.2 to vibe up some webapp ideas for Nexus and I just spent many minutes not having the app render correctly because the agent...</summary>
    <content type="text">&#xA;I am working with MiniMax M.2 to vibe up some webapp ideas for [[Nexus]] and I&#xA;just spent many minutes not having the app render correctly because the agent&#xA;mounted the wrong directory into the docker compose stack. I noticed it about 5&#xA;seconds after opening the file, but wasted nearly a half hour thinking it was a&#xA;docker issue in distrobox...&#xA;&#xA;!!! danger &#34;&#34;&#xA;&#xA;    Clankers are still just clankers man&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>The Irony of 10x</title>
    <id>https://pype.dev/the-irony-of-10x/</id>
    <updated>2026-03-26T06:00:50Z</updated>
    <published>2026-03-26T06:00:50Z</published>
    <link href="https://pype.dev/the-irony-of-10x/" rel="alternate" type="text/html"></link>
    <summary type="text">Agentic coding has been an exciting change to me over the last couple of months specifically. I&#39;ve been using AI tools for a few years now but something...</summary>
    <content type="text">&#xA;## Opener&#xA;&#xA;Agentic coding has been an exciting change to me over the last couple of months&#xA;specifically. I&#39;ve been using AI tools for a few years now but something really&#xA;shifted with Opus 4.5 as well as the tools/harnesses getting better and more&#xA;useful around the same time. I&#39;ve been influenced by people like Simon Willison&#xA;and Steve Yegge who have been on the forefront of agentic coding and [vibe&#xA;engineering](https://simonwillison.net/2025/Oct/7/vibe-engineering/) since the&#xA;dawn of ChatGPT and in my small circles of work I&#39;m definitely on the bloodiest&#xA;bleeding edge of the adoption of these practices. Are they the future? I don&#39;t&#xA;know - I tried to maintain a skeptical posture but until the bubble pops it&#39;s&#xA;looking like this is at least a direction the future of my line of work is&#xA;going.&#xA;&#xA;!!! danger &#34;&#34;&#xA;&#xA;    The tough part is mixing the new world of agentic coding with&#xA;    developers on wildly different points in the spectrum of adoption and maturity.&#xA;&#xA;Here&#39;s a short anecdote about what I mean...&#xA;&#xA;In a new project that I&#39;m on, I&#39;ve refactored/reimplemented a lot of legacy&#xA;code, producing about 140k lines of code, configuration, and&#xA;docs in about 2 months. It&#39;s an insane amount of &#34;product&#34; and I&#39;ve done it&#xA;entirely with agents. But I didn&#39;t do it with a handful of vanilla chat&#xA;sessions like &#34;Hey Copilot, reimplement this API, no mistakes&#34;. I&#39;ve been building&#xA;out my own process for using LLMs effectively. I have no actual idea besides my&#xA;own experience if what I&#39;m building is useful, but it feels pretty good -&#xA;agents for planning, building, reviewing, testing, etc. My &#34;harness&#34; includes&#xA;specialized agents and opinionated development workflows&#xA;to try to ensure that code is never one-shotted into production.&#xA;&#xA;The catch though is that I&#39;m working with a handful of developers and they are not&#xA;early-adopters or aggressive experimenters with these new agentic tools. Most&#xA;of them are still in Steve Yegge&#39;s stage 2 or 3 of AI coding as he&#39;s outlined&#xA;[in this medium&#xA;article introducing&#xA;GasTown](https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04). They&#xA;open a chat session, and say yes or no to Opus. I&#39;ve been in-between stages&#xA;6 and 7 for a while now - managing multiple agentic sessions that themselves&#xA;run specialized subagents primarily for context management, although I don&#39;t&#xA;feel quite ready for full blown stage 8 (agents running agents running agents).&#xA;So this isn&#39;t me saying that I&#39;m&#xA;&#34;better&#34; than the guys I&#39;m working with, just the project we&#39;re on&#xA;together is now being touched by people on wildly different ends of the agentic&#xA;coding spectrum, and the project itself is an experiment to me of living in the &#34;New World&#34;.&#xA;&#xA;Finally the anecdote - I&#39;m getting PRs from guys in stages 2 and 3,&#xA;thousands of lines of code and config, that I know they are not necessarily&#xA;experts in, but they&#39;re producing that code with fairly vanilla practices. How&#xA;do I verify it? I know my own prompts, I know what agents generated my code, I know&#xA;the direction and prodding I gave as they&#39;re building, but all&#xA;I know in a PR review from someone else is the diff - not how they tested it&#xA;(unless of course there&#39;s tests, and there must be, but tests are only as good&#xA;as the tests are...), not the steering they gave, not the manual UAT validation&#xA;they did, or even the full intent... And ultimately I own the code they merge&#xA;because chances are I outlast their contract with the team. It&#39;s kind of a&#xA;scary thing to review and accept... do I trust my&#xA;agentic practices to validate their work? Cause I certainly don&#39;t trust myself&#xA;to validate it perfectly.&#xA;&#xA;!!! note &#34;a note about contractors&#34;&#xA;&#xA;    I&#39;m not implying anything about anyone who works contract, I think the facts is that contractors are typically dis-incentivized to really &#34;own&#34; something - an unfortunate consequence of the world.&#xA;&#xA;## This is about me, not everyone&#xA;&#xA;I want to make sure to reiterate the context of this post and my thoughts -&#xA;it&#39;s really about me. I&#39;m not saying anything here is true for anyone else, I&#39;m&#xA;not making predictions about tomorrow, and I&#39;m not a prominent FOSS developer or a&#xA;high-profile ex-FAANG engineer. I&#39;m not talking about every project under the sun.&#xA;I work at a big company, not a fast-paced startup. I work on internal tooling,&#xA;nothing that should even be seen outside our network. I&#39;m also a self-taught&#xA;developer, not a trained software engineer.&#xA;&#xA;As far as AI goes, lately I&#39;m mostly all in on Github Copilot&#xA;CLI with a sprinkling of Opencode. I&#39;m not using Claude Code or Codex which&#xA;seem to have their own communities around plugins and usage.&#xA;&#xA;And since we&#39;re so focused on me right now, the important thing to keep as the&#xA;backdrop for this post is my temperament and make-up. I&#39;m definitely made a&#xA;certain kind of way, not different than every single person necessarily, but&#xA;different enough, from enough people, to not blend in with the 9-5 guy who can&#xA;log out and not think about work until the morning (I&#39;m not saying that&#39;s a&#xA;strength either, my boundary problems are for another time).&#xA;&#xA;I feel an aggressive burden to solve problems and own those solutions, call it&#xA;white-knighting or a savior complex if you want, but I&#39;ve got enough of a&#xA;reputation at Caterpillar now (and anywhere else I&#39;ve worked) to become a go-to&#xA;person for more than I think is necessarily appropriate. And with that burden&#xA;I&#39;ll bring results - if a problem hooks into my brain it. will. be. solved. I&#xA;probably won&#39;t do the best most clean-code solution right out of the gate, but&#xA;I&#39;ll do whatever I can to find a solution.&#xA;&#xA;## Who I was before agents&#xA;&#xA;That is who I was before agents made code easy to produce. My first boss at&#xA;Caterpillar used the phrase &#34;tenacious learner&#34; to describe me in several&#xA;reviews. I kind of rejected the description because I basically refused to&#xA;believe that I was really any different, any harder of a worker, than my peers.&#xA;&#xA;But with almost a decade of experience in the corporate world, and some adult&#xA;perspective on my life, I think it&#39;s accurate... I am a harder worker, to my&#xA;detriment sometimes, than a fair number of people I&#39;ve worked with\*\*.&#xA;&#xA;I&#39;m not brilliant but I can focus for a long time in the right circumstances.&#xA;The gift of perseverance (or the curse of not being able to let something go,&#xA;depending on how you look at it) has led to blessing in my life in both reward&#xA;and skill.&#xA;&#xA;!!! note &#34;\*\*&#34;&#xA;&#xA;    There&#39;s nothing wrong with it either, Cat&#39;s somewhat noticed that work in my EOY reviews and I&#39;m certainly not against &#34;just doing your job&#34;.&#xA;&#xA;And then AI came along and with another set of the right circumstances&#xA;catalyzed a new way to work.&#xA;&#xA;## Leaning into &#34;Agentic Engineering&#34;&#xA;&#xA;I jumped onto experimenting with AI coding tools as soon as they became&#xA;available, but mostly I just tried vibe coding rather than using tools for real&#xA;engineering work. I vibed up an API at one of my jobs that went into production&#xA;way too early, with far too little validation, and it was scary to support it&#xA;from then on out. I also did the meme, vibe-coded a TODO app, and threw that&#xA;puppy into the internet without locking down my API endpoints... That was&#xA;before agents were quite as useful as they can be&#xA;now, but that experience along with a handful of other stepping stones (like&#xA;learning some real actual fundamentals about security) began to&#xA;give me confidence in using the AI as a tool, like my IDE is a tool, for&#xA;producing **solutions** that take the form of code.&#xA;&#xA;!!! note &#34;&#34;&#xA;&#xA;    As one-shot apps got better and better, and as I learned about scoping work&#xA;    more appropriately for agentic tools my confidence in them grew.&#xA;&#xA;There&#39;s quite a difference between &#34;Claude make me a todo app, no mistakes&#34; and&#xA;scoping out a solution in natural language, with some technical guardrails, and&#xA;having agents tackle the implementation methodically.&#xA;&#xA;## What actually changed&#xA;&#xA;What&#39;s actually changed for me is quite a lot... I haven&#39;t opened my IDE to&#xA;seriously write code for months now. I&#39;ve oscillated between Opencode and&#xA;Copilot CLI, leaning moreso into Copilot since it&#39;s an approved tool at work&#xA;and as of mid-February is quite good. Mentally I&#39;m approaching problems with a&#xA;little more thought on the front-end than before because prior to agents I&#xA;would think as I implemented. At the scale of work that I do, this was really&#xA;fine - working on CLI utilities to solve simple problems, developing an&#xA;iterative testing cycle for each problem that allowed me to move fast, and once&#xA;I found a groove I was cooking. But now I don&#39;t even need to find it, I open&#xA;Opencode or Copilot CLI with my Planner agent, describe what I want to happen and have&#xA;Opus or GPT scope out a plan for me. Usually there&#39;s some back and forth on&#xA;feature scoping, then I review a markdown file it produces, and once it looks&#xA;decent enough to me I say &#34;go&#34; and it goes.&#xA;&#xA;That works a lot better than I even care to admit because at the same time as&#xA;I&#39;ve been leaning harder into agents, I&#39;ve been building my own harness of&#xA;sorts - not a replacement for Copilot CLI or a competitor to Opencode, but&#xA;moreso an opinionated workflow spine that I force agents into to give strict&#xA;gates to the SDLC (software development lifecycle).&#xA;&#xA;!!! warning &#34;Problem Solving Workflows&#34;&#xA;&#xA;    Plan and implement is fine for a lot of things, and I do think it&#39;s only getting better. My harness,&#xA;    mentioned a few times around here before, called Nexus, is a set of agents and&#xA;    rules that I want the code I&#39;ll be responsible for to go through before it&#xA;    lands in production. That cycle isn&#39;t too complex, and there&#39;s only about&#xA;    10,000 similar tools to Nexus on Github trending right now. I&#39;ve thought about dropping my idea and picking up&#xA;    something more popular, like&#xA;    [superpowers](https://www.github.com/obra/superpowers) but at the moment I&#39;m&#xA;    continuing to develop on and lean into my own idea here.&#xA;&#xA;!!! note &#34;Mini post on Nexus&#34;&#xA;&#xA;    I keep saying a blog post is coming, but the high level of Nexus is that it&#39;s a task&#xA;    tracker with a CLI that agents use to advance a ticket through a plan -&gt; build&#xA;    -&gt; test -&gt; review -&gt; verify -&gt; merge lifecycle that is almost exactly how I&#xA;    would otherwise have solved a problem by hand. I think it needs work, I need to&#xA;    be harder on TDD methodlogies with agents, and work on verification gating a&#xA;    bit more (shoutout to [showboat](https://github.com/simonw/showboat) by Simon&#xA;    Willison) but overall it&#39;s a system of thinking that I already participate in&#xA;    so I&#39;m doing my best to farm out specific parts of my workflow to agents rather&#xA;    than trying to one-shot enterprise problems and solutions.&#xA;&#xA;!!! danger &#34;Who&#39;s doing the thinking?&#34;&#xA;&#xA;    I&#39;ve noticed that as I&#39;ve developed Nexus out though, I lean on the agents for&#xA;    more and more of my own thinking, and am trusting my problem solving&#xA;    **process** moreso than my actual problem solving abilities.&#xA;&#xA;## Hidden costs&#xA;&#xA;The cost of this increase in speed is a lack of familiarity - and the fallout&#xA;of lack of familiarity is hard to express. There&#39;s also many facets to it.&#xA;For me, the first facet is that Nexus helps me move fast, but as I&#39;ve leaned&#xA;into it for more and more of the planning, I&#39;m less and less familiar with the&#xA;state of the code. I find myself asking my reviewer agents in fresh sessions&#xA;often to explain it to me, and thankfully they&#39;re usually consistent, but&#xA;nonetheless I&#39;m still not intimately familiar with the code. And on Nexus it&#39;s&#xA;not a big deal, that&#39;s low stakes, it&#39;s just me and my workflow.&#xA;&#xA;I&#39;m using Nexus + Copilot at work and that feels like higher&#xA;stakes... I have my agents explain the status of our project and although they&#39;re&#xA;also somewhat consistent the thing that&#39;s scary is that other people are&#xA;working on that repo with me, and that&#39;s where another layer of complexity&#xA;manifests itself. If it&#39;s just me and my [[clankers]], let&#39;s go all day long,&#xA;rebuild, ask questions, etc... but I have other developers I rub shoulders with&#xA;now, and if they ask me a question what am I going to say? &#34;Hold on, let me prompt&#xA;my agent for you&#34; - it&#39;s LMGTFY on steroids. And the burden becomes if I&#xA;feel like I can own and support what those other developers push into the repo.&#xA;&#xA;## Murky responsibility boundaries&#xA;&#xA;Why do I own their work? Well for the third time, this post is pretty&#xA;self-centered and all about me, and my situation is that the other developers I&#xA;work with presently are all contractors. Their work agreement with Cat could&#xA;end at any second, for practically any reason. The incentive structure isn&#39;t&#xA;there for these guys who technically work for an agency... Their bonuses aren&#39;t&#xA;bigger (or even exist) if Cat performs well, there&#39;s no extra vacation days in&#xA;it for em (aye, contractors don&#39;t get vacation days anyways), and not that it&#39;s&#xA;a problem, moreso just the nature of the world we&#39;re in - but they&#39;re basically&#xA;mercenaries out to the highest bidder and I happen to know of **multiple times&#xA;where Cat lost a good person to a higher bidder**.&#xA;&#xA;So this isn&#39;t really me trying to be negative about contractors at all, I&#39;m&#xA;here for a pay-check as well but Cat at least gives me SOME incentive to work&#xA;hard with the goal of compensation regardless of how altruistic I feel in my&#xA;own circumstances.&#xA;&#xA;!!! note &#34;Incentive&#34;&#xA;&#xA;    Better ratings mean marginally better end-of-year salary increases, and I&#39;ve received some other awards that certainly give me pause about jumping ship to another long-term place even when things can be crazy at Cat.&#xA;&#xA;It&#39;s more than just the contrator-ownership dilema, I&#39;ve dug myself quite a&#xA;hole over the last 8-10 years, gaining a reputation that I think many would&#xA;appreciate, but for me only lately increases the stress. I don&#39;t need to parrot&#xA;every accolade I&#39;ve ever received, that&#39;s not the point, but to make the point&#xA;as clear as I can - I have a lot of respect from quite a few people at&#xA;Caterpillar. I&#39;m blessed to have that reputation, and it&#39;s not like I haven&#39;t&#xA;worked hard for it - but people talk about me in a way so flattering I feel&#xA;like the main character in a fictional story sometimes.&#xA;&#xA;In a fictional story I can check out the ending, hit up spark notes, or ask AI&#xA;how it ends... but there isn&#39;t an &#34;end&#34; in my real world scenario, there&#39;s only&#xA;tomorrow and I feel the pressure of not knowing what tomorrow holds now more&#xA;than ever.&#xA;&#xA;!!! danger &#34;&#34;&#xA;&#xA;    Being noticed is starting to feel more costly than rewarding...&#xA;&#xA;## Financial irony&#xA;&#xA;What&#39;s the cost? It&#39;s hard to get specific without writing a novel but here&#39;s&#xA;the TLDR - because I&#39;ve been pretty good at what I do I&#39;ve been able to do this&#xA;type of work outside my normal 9-5 responsibilities and with that extra work&#xA;has been some pretty great financial benefits. However with Cat changes,&#xA;responsibility increases, and now owning code that others (and their clankers)&#xA;write, the extra time I gained for myself is eaten-up and has been reclaimed by&#xA;the mega-corp... &#34;Exceeding expectations&#34; every year just meant the bar is&#xA;raised, the expectations are higher, the time-commitment requirement is higher,&#xA;and as I&#39;ve had to meet the requirements of both the new world and the curse of&#xA;being noticed, I&#39;ve lost the time for the extra work... For years I&#39;ve realized&#xA;the benefit of my own skills and drive, but the irony of agents (and a handful&#xA;of other things) is that with the dramatic increase in expectations, not only&#xA;on me but on those I work with and therefore their output, I don&#39;t get to&#xA;realize the benefits of my own gifts anymore.&#xA;&#xA;## Meaning and fatigue&#xA;&#xA;I feel very torn because the work I&#39;ve been called into with Cat is good, I&#xA;said in [[cat-autonomy-2-0]] that autonomy will save people&#39;s lives. I love&#xA;getting to participate in that mission, it&#39;s the primary reason I didn&#39;t jump&#xA;ship to try to maintain the levels, and type, of work I was doing before... But&#xA;in a few short months the mission is being drowned out by expectations and&#xA;requirements that are so high I&#39;m losing the grip on my own life.&#xA;&#xA;## Open questions&#xA;&#xA;That leads me to questions that I can&#39;t answer, the question I ask daily now of&#xA;&#34;What about tomorrow?&#34;. What will agents do for us tomorrow, what problems will&#xA;be solved, what bugs will I create (by agents of course because I&#39;ve never&#xA;written a bug by hand in my whole life \s). If I stopped using agents would&#xA;people still be impressed? Would it even matter?&#xA;&#xA;## Fin&#xA;&#xA;I&#39;m certainly not anti-AI, it&#39;s typing all my code. I&#39;m not anti-collaboration,&#xA;although I do wish I could work alone with just my clanker-army to worry about.&#xA;I&#39;m not sure what I am anymore though... AI has changed how I work, what I work&#xA;on, and who I work with... Everything has changed in such a short period of&#xA;time and like the ending of this post, it&#39;s pretty jarring.&#xA;&#xA;!!! danger &#34;&#34;&#xA;&#xA;    Death comes to us all - James Acaster.&#xA;&#xA;Thanks for reading.&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Remote Terraform State Requires Working Traefik… DUH!</title>
    <id>https://pype.dev/remote-terraform-state-requires-working-traefik-duh/</id>
    <updated>2026-03-23T08:12:25Z</updated>
    <published>2026-03-23T08:12:25Z</published>
    <link href="https://pype.dev/remote-terraform-state-requires-working-traefik-duh/" rel="alternate" type="text/html"></link>
    <summary type="text">I&#39;m working on some spring cleaning in my homelab and backed myself into a hilarious corner yesterday. I use Open Tofu for any of my Terraform needs now, and...</summary>
    <content type="html">&lt;p&gt;I&amp;rsquo;m working on some spring cleaning in my homelab and backed myself into a&#xA;hilarious corner yesterday. I use Open Tofu for any of my Terraform needs now,&#xA;and although I don&amp;rsquo;t manage a ton with terraform, I do manage all my cloudflare&#xA;stuff with it. I decided I wanted to use my own minio instance as the s3 remote&#xA;state backend for my workspaces so I could rely on my typical NAS data&#xA;backup/retention workflow for the buckets in case anything went wrong, as&#xA;opposed to a local state file that I&amp;rsquo;m not taking a lot of precautions with.&#xA;Well during my Spring Cleaning I was working towards replacing ingress into my&#xA;home network with Cloudflare tunnels and in the midst of that update I took&#xA;down traefik, no matter a simple &amp;rsquo;tofu apply&amp;rsquo; should get me right back to&#xA;working order&amp;hellip;&lt;/p&gt;&#xA;&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;╷&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;│&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;Error&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;Error&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;inspecting&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;states&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;the&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;s3&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;backend&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;│&lt;/span&gt;     &lt;span class=&#34;k&#34;&gt;operation&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;S3&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;ListObjectsV2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;https&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;response&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;StatusCode&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;RequestID&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;HostID&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;api&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;NotFound&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;Not&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;Found&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hilarious problem with thankfully an easy fix&amp;hellip; downloading the state file&#xA;from Minio wasn&amp;rsquo;t a big deal since the container was still running without&#xA;issue, and placing the state file in the folder to use as the local state&#xA;solution for the interim went totally smooth, but this highlights the set of&#xA;interdependencies I&amp;rsquo;m creating for myself and as I take the next few days/weeks&#xA;to do some spring cleaning I&amp;rsquo;m hoping I can separate out the external ingress&#xA;from internal with a bit more clear boundaries so that I never lock myself out&#xA;of a workflow I only execute on my LAN in the first place!&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>To Live In A World Without AI</title>
    <id>https://pype.dev/to-live-in-a-world-without-ai/</id>
    <updated>2026-03-22T14:50:38Z</updated>
    <published>2026-03-22T14:50:38Z</published>
    <link href="https://pype.dev/to-live-in-a-world-without-ai/" rel="alternate" type="text/html"></link>
    <summary type="text">I&#39;m finding lately that I wish we could go back to pre-ChatGPT... A world without a code-gen easy button, where &#34;easy&#34; was LSP autocomplete, where tools were...</summary>
    <content type="html">&lt;p&gt;I&amp;rsquo;m finding lately that I wish we could go back to pre-ChatGPT&amp;hellip; A world&#xA;without a code-gen easy button, where &amp;ldquo;easy&amp;rdquo; was LSP autocomplete, where tools&#xA;were at my fingertips rather than remote inference endpoints, and where I and&#xA;everyone I worked with, was paid and judged based on what we could produce with&#xA;our own 2 hands, even if &lt;code&gt;ctrl + c&lt;/code&gt; and &lt;code&gt;ctrl + v&lt;/code&gt; was sometimes a common set of keys&amp;hellip; it&#xA;sure beat answering &amp;ldquo;yes&amp;rdquo; to a robot every 38 seconds.&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>paynepride dot com outage on vacation</title>
    <id>https://pype.dev/paynepride-dot-com-outage-on-vacation/</id>
    <updated>2026-03-16T13:21:31Z</updated>
    <published>2026-03-16T13:21:31Z</published>
    <link href="https://pype.dev/paynepride-dot-com-outage-on-vacation/" rel="alternate" type="text/html"></link>
    <summary type="text">The day after I leave for vacation I start getting SSL errors on every homelab service I host for myself and others. The culprit was my Cloudflare API token...</summary>
    <content type="html">&lt;p&gt;The day after I leave for vacation I start getting SSL errors on every homelab&#xA;service I host for myself and others. The culprit was my Cloudflare API token&#xA;expiring. It was easy to find the 403s in the logs for Traefik (thank goodness&#xA;for Tailscale getting me into the lab from afar). The solution was to rotate the&#xA;API token, replace the value in Traefik&amp;rsquo;s .env file, and hit it with the &amp;ldquo;just&#xA;deploy&amp;rdquo; button. Now I don&amp;rsquo;t know why this expired - the key looks like it has&#xA;no expiration to me - and I&amp;rsquo;m too tired from the beach to dig in further.&#xA;Until next time, I expect this error to come back March 16 2027 I suppose.&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>My Thoughts on Beads</title>
    <id>https://pype.dev/my-thoughts-on-beads/</id>
    <updated>2026-03-03T05:00:47Z</updated>
    <published>2026-03-03T05:00:47Z</published>
    <link href="https://pype.dev/my-thoughts-on-beads/" rel="alternate" type="text/html"></link>
    <summary type="text">Steve Yegge is a pretty well-known individual in the tech field, having been around for a long time at some of the larger companies. He&#39;s making quite a...</summary>
    <content type="text">&#xA;[Steve Yegge](https://en.wikipedia.org/wiki/Steve_Yegge) is a pretty well-known individual in the tech field, having been&#xA;around for a long time at some of the larger companies. He&#39;s making quite a&#xA;splash in the agentic coding world as well. I&#39;ve appreciated Steve&#39;s posts and&#xA;projects lately and wanted to put some thoughts on one called&#xA;[beads](https://github.com/steveyegge/beads).&#xA;&#xA;## Beads&#xA;&#xA;Beads is an issue tracker with links - issues relate to and block each other,&#xA;but agents use beads to keep track of information and dependencies without&#xA;storing it in their context 100% of the time. It seems like a very popular and&#xA;useful tool - but I am not using it, and that&#39;s what I wanted to capture... why&#xA;not?&#xA;&#xA;The answer for me is about **where** the organization layer is for the&#xA;developer. Beads exists in a single repo - it&#39;s a system-wide CLI but you &#39;bd&#xA;init&#39; in a git repo, and beads uses the `.git/` folder, worktrees, [dolt](https://docs.dolthub.com/), and some&#xA;git hooks to operate within that git repo. Outside the repo, it takes another&#xA;tool to tie together all the beads databases you might have.&#xA;&#xA;For me, I&#39;m hardly &#34;in&#34; a git repo anymore. My workflow is that when I have&#xA;something to work on, I create a &#34;workspace&#34; ([self-defined concept](https://pypeaday.github.io/dotfiles/terminal/workspaces/#installation)) which is&#xA;just a folder on my filesystem where I check-out git worktrees from any of the&#xA;repos related to the work I&#39;m doing. Sometimes it&#39;s 1 worktree from 6 repos,&#xA;sometimes it&#39;s 6 worktrees from 1 repo for parallel work...&#xA;&#xA;So because I like to organize myself in this way, beads is already &#34;out&#34; for&#xA;me. That&#39;s the main reason - I don&#39;t have any real technical issues with beads&#xA;or any criticism, it just is designed for a workflow that is not how I work.&#xA;&#xA;This is why I&#39;m building [[nexus]], something I hope to be able to put out&#xA;there &#34;soon&#34;. It won&#39;t be as general-purpose as beads, but my goal with it is&#xA;to be plug-and-play for any agentic harness (copilot cli, claude code,&#xA;opencode, etc.). It&#39;s a challenge thinking about it as a personal tool but also&#xA;as a tool to share someday, but agentic coding is making it possible to make&#xA;some cool shareable stuff and I&#39;m excited for my own workflow-task-manager to&#xA;mature and at least become something useful to me (it already is, but building&#xA;the plane in the air makes it kind of hard to enjoy the plane).&#xA;&#xA;### Credit&#xA;&#xA;- banner image from ChatGPT&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Forge Ahead</title>
    <id>https://pype.dev/forge-ahead/</id>
    <updated>2026-02-17T05:14:15Z</updated>
    <published>2026-02-17T05:14:15Z</published>
    <link href="https://pype.dev/forge-ahead/" rel="alternate" type="text/html"></link>
    <summary type="text">Yesterday&#39;s reflection-contentment-and-work has a second-part this morning. As I was wrapping up a project I didn&#39;t realize the closed-off-ness of leaving......</summary>
    <content type="text">&#xA;Yesterday&#39;s [[reflection-contentment-and-work]] has a second-part this morning.&#xA;As I was wrapping up a project I didn&#39;t realize the closed-off-ness of&#xA;leaving... I handed in some notes, and mid-message to someone the clock struck&#xA;midnight and I was locked out. It&#39;s fitting to be honest, and now in the wake&#xA;of yesterday&#39;s contentment post, experiencing some more loss than I expected,&#xA;today we forge ahead.&#xA;&#xA;My main focus for work now is a project I will refer to as `forge`. It is what&#xA;I will bring to Caterpillar Autonomy but I can only build it thanks to the&#xA;gifts and experience God has given me. The thing I want to put on paper is a&#xA;short list of experiences I think God in his sovereignty, gave me over the last&#xA;few years and what they provide for me for Forge.&#xA;&#xA;## Cat Reman Platform&#xA;&#xA;One of the first things that comes to mind is another project I was sad to&#xA;lose: a platform I started for an analytics team in Cat Reman. That began as a&#xA;simple python cli to automate some developer operations that was otherwise a&#xA;dozen clicks through the AWS console. Using [boto3] and some simple python I&#xA;gave that team the start of some real velocity gains. Eventually that grew into&#xA;a larger kubernetes-based service where the data science operations: code quality,&#xA;deployment, updates, webapps, etc. were all handled by our platform. It wasn&#39;t&#xA;perfect, it wasn&#39;t self-contained, it was a set of things kind of glued&#xA;together with systems and scripts, but it worked, it works today and is under&#xA;fantastic ownership.&#xA;&#xA;What I learned just from starting that cli was to be passionate about solving&#xA;problems. No one asked me to make it, but it needed made, and the team is in&#xA;such a better place for me having started it.&#xA;&#xA;## Kedro and OpenShift&#xA;&#xA;Another short project I was able to participate in a few years ago was leading&#xA;a data-syncronization task into a fiery horrific crash that lasted weeks -&#xA;neigh months longer - than was necessary or appropriate... The details aren&#39;t&#xA;relevant - I was the lead dev in a new place tasks with syncing up data between&#xA;2 applications. Ultimately, could&#39;ve been a python script but I over-designed a&#xA;kedro-based solution because of some requirements I misunderstood. Part of that&#xA;misunderstanding was not knowing how to get the requirements I needed, but they&#xA;weren&#39;t provided in full, I didn&#39;t know any better, so for weeks we were a&#xA;corporate meme trying to use python to update a database that no one on our&#xA;team understood if we were allowed to write to.... It was very confusing.&#xA;&#xA;But what I learned was a lot about gathering requirements, questioning&#xA;assumptions, and the importance of understanding your constraints as fully as&#xA;possible as early as possible.&#xA;&#xA;## A Real Platform&#xA;&#xA;Then one of the biggest blessings I see for Forge, is the experience I&#39;ve&#xA;gotten recently with AWS at scale... Not 1 or 2 services created with&#xA;Medium.com copy pasta tutorials from docs... but experience working in a large&#xA;project across many accounts, supporting several teams, using a wide variety of&#xA;technologies from Terraform to Kubernetes.&#xA;&#xA;On this project I learned about practical system design and gained a lot of&#xA;confidence in supporting systems that are complicated... I&#39;m not the smartest&#xA;guy in the world but I&#39;m no dummy, and even while lacking fluency in the system&#xA;I was supporting on this project I learned a lot of troubleshooting skills and&#xA;gained confidence in my ability to troubleshoot complex systems.&#xA;&#xA;I&#39;ll need this for Forge - which will break certainly, but I&#39;ll be there to fix&#xA;it and I&#39;m sure I can because I&#39;ve done these other things.&#xA;&#xA;## Fin&#xA;&#xA;The Lord has been with me through these projects - he has certainly blessed me&#xA;with a skillset and personality that lends itself to being really useful in the&#xA;Tech world I&#39;ve landed in (which is a whole &#39;nother story of God&#39;s sovereignty&#xA;and provision). I am very grateful for the swath of experience I&#39;ve gotten over&#xA;the last 8 years or so, and this season I&#39;m in of change is rocking me a little&#xA;more than I anticipated but by God&#39;s grace I think I see the purpose, or at&#xA;least **a** purpose, and I pray I am making the choices for work he wants me to&#xA;make.&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Kubernetes External Secrets Operator</title>
    <id>https://pype.dev/kubernetes-external-secrets-operator/</id>
    <updated>2026-02-10T07:48:34Z</updated>
    <published>2026-02-10T07:48:34Z</published>
    <link href="https://pype.dev/kubernetes-external-secrets-operator/" rel="alternate" type="text/html"></link>
    <summary type="text">I wanted to put a short demo together of using External Secrets Operator (ESO) to expose secrets from a vault (like Hashicorp Vault, AWS Secrets Manager,...</summary>
    <content type="text">&#xA;I wanted to put a short demo together of using External Secrets Operator (ESO)&#xA;to expose secrets from a vault (like Hashicorp Vault, AWS Secrets Manager, etc)&#xA;to services running in kubernetes&#xA;&#xA;Demo code is [here in this github repo](https://github.com/pypeaday/blog-kubernetes-external-secrets-operator-demo)&#xA;&#xA;This post is a high level overview of the components, see the repo for the full example.&#xA;&#xA;## Setup&#xA;&#xA;- [[docker]] for containerized development&#xA;- [[kind]] for setting up a quick cluster&#xA;- [[kubectl]] for accessing the cluster&#xA;- [[helm]] for installing ArgoCD and ESO&#xA;&#xA;- and then [justfile](https://github.com/casey/just) is there to wrap the commands to easier execution&#xA;&#xA;## Step 0 - Vault&#xA;&#xA;- for the demo we&#39;ll setup Hashicorp Vault in docker compose to easily bring it&#xA;  up and down&#xA;- and the init-script is in the repo - it uses curl to make some secrets in&#xA;  vault that we&#39;ll reference later&#xA;&#xA;```yml&#xA;services:&#xA;  vault:&#xA;    image: hashicorp/vault:1.18&#xA;    container_name: vault&#xA;    ports:&#xA;      - &#34;58200:8200&#xA;    environment:&#xA;      VAULT_DEV_ROOT_TOKEN_ID: root&#xA;      VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200&#xA;    volumes:&#xA;      - vault-data:/vault/file&#xA;    cap_add:&#xA;      - IPC_LOCK&#xA;    command: server -dev -dev-root-token-id=root&#xA;&#xA;volumes:&#xA;  vault-data:&#xA;```&#xA;&#xA;- bringing up the vault instance is a simple `docker compose up` (use the just recipes which some `curl` commands for checking status etc.)&#xA;&#xA;```bash&#xA;curl -s http://localhost:58200/v1/sys/health | jq .  # or just vault-status&#xA;{&#xA;  &#34;initialized&#34;: true,&#xA;  &#34;sealed&#34;: false,&#xA;  &#34;standby&#34;: false,&#xA;  &#34;performance_standby&#34;: false,&#xA;  &#34;replication_performance_mode&#34;: &#34;disabled&#34;,&#xA;  &#34;replication_dr_mode&#34;: &#34;disabled&#34;,&#xA;  &#34;server_time_utc&#34;: 1770893657,&#xA;  &#34;version&#34;: &#34;1.18.5&#34;,&#xA;  &#34;enterprise&#34;: false,&#xA;  &#34;cluster_name&#34;: &#34;vault-cluster-acfb9930&#34;,&#xA;  &#34;cluster_id&#34;: &#34;4d9162f4-e501-371b-7f94-bd60052b40a3&#34;,&#xA;  &#34;echo_duration_ms&#34;: 0,&#xA;  &#34;clock_skew_ms&#34;: 0,&#xA;  &#34;replication_primary_canary_age_ms&#34;: 0&#xA;}&#xA;```&#xA;&#xA;## Step 1 - App&#xA;&#xA;- We need an app that requires secrets&#xA;- app code in repo, essentially it&#39;s a python webserver to show the vault&#xA;  values (obviously this would expose real secrets so it&#39;s just a demo)&#xA;- Below is one of the endpoints in the vibe-coded app, just to illustrate that&#xA;  we&#39;re going to give secrets to the app as environment variables (or as mounted&#xA;  files!)&#xA;- In the repo, the app is included and there&#39;s a `just build` which builds the docker image&#xA;- There is also `just deploy` which handles loading the image into `kind`&#39;s image cache&#xA;&#xA;```py&#xA;&#xA;# example route from demo-app - see git repo&#xA;@app.get(&#34;/env&#34;, response_class=HTMLResponse)&#xA;def show_env():&#xA;    # ESO brings Vault secrets into environment variables&#xA;    env_vars = dict(os.environ)&#xA;&#xA;    # Sort by category, then by key&#xA;    sorted_items = sorted(env_vars.items(), key=lambda x: (classify_env(x[0]), x[0]))&#xA;&#xA;    cards = &#34;&#34;.join(create_card(k, v) for k, v in sorted_items)&#xA;&#xA;    secret_count = sum(1 for k in env_vars if k in SECRET_KEYS)&#xA;    config_count = sum(1 for k in env_vars if k in CONFIG_KEYS)&#xA;    system_count = len(env_vars) - secret_count - config_count&#xA;&#xA;    html = HTML_TEMPLATE.format(&#xA;        cards=cards,&#xA;        secret_count=secret_count,&#xA;        config_count=config_count,&#xA;        system_count=system_count,&#xA;    )&#xA;&#xA;    return HTMLResponse(content=html)&#xA;&#xA;def read_mounted_files(directory: str) -&gt; dict:&#xA;    &#34;&#34;&#34;Read all files from a mounted directory.&#34;&#34;&#34;&#xA;    files_data = {}&#xA;    if os.path.exists(directory) and os.path.isdir(directory):&#xA;        for filename in os.listdir(directory):&#xA;            filepath = os.path.join(directory, filename)&#xA;            if os.path.isfile(filepath):&#xA;                try:&#xA;                    with open(filepath, &#34;r&#34;) as f:&#xA;                        files_data[filename] = f.read().strip()&#xA;                except Exception as e:&#xA;                    files_data[filename] = f&#34;&lt;Error reading file: {e}&gt;&#34;&#xA;    return files_data&#xA;&#xA;```&#xA;&#xA;## Step 2 - Cluster&#xA;&#xA;- use `kind` to bring up a cluster&#xA;- this will start a few docker containers to act as your control-plane and workers&#xA;&#xA;```yml&#xA;# kind-config.yml&#xA;kind: Cluster&#xA;apiVersion: kind.x-k8s.io/v1alpha4&#xA;name: eso-demo&#xA;nodes:&#xA;  - role: control-plane&#xA;    extraPortMappings:&#xA;      - containerPort: 30080&#xA;        hostPort: 58080&#xA;        protocol: TCP&#xA;  - role: worker&#xA;```&#xA;&#xA;```&#xA;kind create cluster --config kind-config.yaml --name eso-demo&#xA;```&#xA;&#xA;## Step 3 - External Secrets Operator&#xA;&#xA;- installed with [[helm]] from the official helm chart&#xA;- NOTE: this is the Operator, not the secrets... this is the thing which goes&#xA;  to the secrets backend and creates kubernetes secrets&#xA;&#xA;```&#xA;helm repo add external-secrets https://charts.external-secrets.io 2&gt;/dev/null || true&#xA;helm repo update&#xA;helm install external-secrets external-secrets/external-secrets \&#xA;  --namespace external-secrets \&#xA;  --create-namespace \&#xA;  --wait&#xA;```&#xA;&#xA;In the repo this is mostly `just eso-install`&#xA;&#xA;## Step 3.5 - Secretstore&#xA;&#xA;- You need a `clustersecretstore` to be the place that ESO puts secrets&#xA;&#xA;```&#xA;apiVersion: external-secrets.io/v1&#xA;kind: ClusterSecretStore&#xA;metadata:&#xA;  name: vault-backend&#xA;spec:&#xA;  provider:&#xA;    vault:&#xA;      server: &#34;http://10.10.0.1:58200&#34;&#xA;      path: &#34;secret&#34;&#xA;      version: &#34;v2&#34;&#xA;      auth:&#xA;        tokenSecretRef:&#xA;          name: vault-token&#xA;          key: token&#xA;          namespace: external-secrets&#xA;&#xA;```&#xA;&#xA;## Step 4 - Secrets&#xA;&#xA;- Secrets go in the `clustersecretstore`&#xA;  - in this example it&#39;s called &#39;vault-backend&#39;&#xA;- In the demo we can just `kubectl apply -f &lt;manifest&gt;` to deploy the secret to&#xA;  the cluster&#xA;- In practice this should be handled by something more mature than raw-doggin&#xA;  kubectl commands&#xA;&#xA;```yml&#xA;# manifests/external-secrets.yml&#xA;---&#xA;apiVersion: external-secrets.io/v1&#xA;kind: ExternalSecret&#xA;metadata:&#xA;  name: demo-app-secrets&#xA;  namespace: default&#xA;spec:&#xA;  refreshInterval: &#34;10s&#34;&#xA;  secretStoreRef:&#xA;    kind: ClusterSecretStore&#xA;    name: vault-backend&#xA;  target:&#xA;    name: demo-app-secrets&#xA;    creationPolicy: Owner&#xA;  data:&#xA;    - secretKey: DATABASE_PASSWORD&#xA;      remoteRef:&#xA;        key: secret/data/demo-app/secrets&#xA;        property: database_password&#xA;    - secretKey: API_KEY&#xA;      remoteRef:&#xA;        key: secret/data/demo-app/secrets&#xA;        property: api_key&#xA;```&#xA;&#xA;## Step 4.1 - Files&#xA;&#xA;- ESO supports mounting files to containers as well through special `ExternalSecret` resources&#xA;- One of the example seecrets is a TLS certificate&#xA;&#xA;```yml&#xA;# manifests/external-secrets-files.yml&#xA;---&#xA;# File-based ExternalSecret for TLS certificates&#xA;# These will be mounted as files in /etc/secrets/&#xA;apiVersion: external-secrets.io/v1&#xA;kind: ExternalSecret&#xA;metadata:&#xA;  name: demo-app-tls-files&#xA;  namespace: default&#xA;spec:&#xA;  refreshInterval: &#34;10s&#34;&#xA;  secretStoreRef:&#xA;    kind: ClusterSecretStore&#xA;    name: vault-backend&#xA;  target:&#xA;    name: demo-app-tls-files&#xA;    creationPolicy: Owner&#xA;    # Template to ensure proper file formatting&#xA;    template:&#xA;      type: Opaque&#xA;      data:&#xA;        tls.crt: &#34;{{ .tls_crt }}&#34;&#xA;        tls.key: &#34;{{ .tls_key }}&#34;&#xA;  data:&#xA;    - secretKey: tls_crt&#xA;      remoteRef:&#xA;        key: secret/data/demo-app/tls-files&#xA;        property: tls.crt&#xA;    - secretKey: tls_key&#xA;      remoteRef:&#xA;        key: secret/data/demo-app/tls-files&#xA;        property: tls.key&#xA;```&#xA;&#xA;- Notice how there&#39;s a `spec.target.template` which templates out the file&#xA;  contents from the secret contents&#xA;&#xA;## Step 5 - Helm Chart&#xA;&#xA;- This isn&#39;t about setting up a helm chart so I&#39;m not going to explain a lot&#xA;  but the working example is simple, not secure, and in the repo&#xA;- The helm chart renders manifests - I&#39;ve paired one down and added comments to&#xA;  the relevant things&#xA;- The thing to just take note of is the reference of the secrets in the `envFrom` section&#xA;&#xA;```yml&#xA;# deployment.yml&#xA;apiVersion: apps/v1&#xA;kind: Deployment&#xA;metadata:&#xA;  annotations:&#xA;    meta.helm.sh/release-name: demo-app&#xA;    meta.helm.sh/release-namespace: default&#xA;  name: demo-app&#xA;  namespace: default&#xA;spec:&#xA;  replicas: 1&#xA;  template:&#xA;    metadata:&#xA;      labels:&#xA;        app.kubernetes.io/instance: demo-app&#xA;        app.kubernetes.io/name: demo-app&#xA;    spec:&#xA;      containers:&#xA;        - envFrom:&#xA;            - secretRef:&#xA;                name: demo-app-secrets # name of example secret from section 4&#xA;            - secretRef:&#xA;                name: demo-app-config # another example in the repo&#xA;          image: demo-app:latest # the image you built and loaded into kind - simple &#39;just&#39; recipe in the repo&#xA;          imagePullPolicy: Never&#xA;          name: demo-app&#xA;          volumeMounts:&#xA;            - mountPath: /etc/secrets&#xA;              name: secrets-volume&#xA;              readOnly: true&#xA;            - mountPath: /etc/config&#xA;              name: configs-volume&#xA;              readOnly: true&#xA;      volumes:&#xA;        - name: secrets-volume&#xA;          secret:&#xA;            defaultMode: 420&#xA;            secretName: demo-app-tls-files # example secret file from section 4.1&#xA;        - name: configs-volume&#xA;          secret:&#xA;            defaultMode: 420&#xA;            secretName: demo-app-config-files&#xA;```&#xA;&#xA;## Step 5.1 - Deploy&#xA;&#xA;- We can deploy the demo-app from the git repo to the cluster&#xA;- For a local demo a few things happen&#xA;  - local image build&#xA;  - loading that image into [[kind]] (`kind` doesn&#39;t have access to your host&#39;s docker image cache, so images need to be loaded into the cluster cache)&#xA;- `just deploy` takes care of this for you, read the recipe in the repo if&#xA;  you&#39;re interested in more there, the focus of this post and example are to&#xA;  briefly show how to use ESO though&#xA;&#xA;## Step 6 - Profit&#xA;&#xA;The example app just displays things that are mounted in - totally vibe-coded&#xA;to illustrate the secrets mounting, not the appropriate way to leak secrets.&#xA;&#xA;![20260210233804_614254b7.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20260210233804_614254b7.png)&#xA;&#xA;![20260210233828_19852225.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20260210233828_19852225.png)&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Cat Autonomy 2.0</title>
    <id>https://pype.dev/cat-autonomy-2-0/</id>
    <updated>2026-02-09T06:07:12Z</updated>
    <published>2026-02-09T06:07:12Z</published>
    <link href="https://pype.dev/cat-autonomy-2-0/" rel="alternate" type="text/html"></link>
    <summary type="text">I wrote about my new role new-job-caterpillar-autonomy a bit a couple weeks ago during an insanely busy time - having just started the role and wrapping up...</summary>
    <content type="text">&#xA;I wrote about my new role [[new-job-caterpillar-autonomy]] a bit a couple weeks&#xA;ago during an insanely busy time - having just started the role and wrapping up&#xA;what ended up being side-work, paired with some sleep-deprived ADHD hyperfocus&#xA;on my new responsibilities and [[nexus]], that post is less of an update and&#xA;more of a mind-dump. This post is meant to be a calmer update about my upcoming&#xA;time with Caterpillar Autonomy.&#xA;&#xA;It starts, like all good stories, with Caterpillar&#39;s RTO (return to office)&#xA;mandate and how it has affected a lot of people negatively (in my opinion). I&#xA;haven&#39;t seen one positive remark about it fromanyone making less than $250k a&#xA;year + Bonus + Equity if you catch my drift...&#xA;&#xA;I know of people who really did lose their jobs at the end of 2025 for refusing&#xA;to move. My story with RTO is covered elsewhere, I&#39;d been blessed with remote&#xA;work and was even secure in a remote position in the face of Caterpillar being&#xA;willing to fire me [[after-exceeding-expectations-for-4-years]].&#xA;&#xA;But time passes and life happens, and the sovereign Lord brought me to this current&#xA;circumstance: where I&#39;m fully committed to Caterpillar Autonomy, and it&#xA;feels weird to say it. I&#39;m going to skip the RTO details, if you know you know.&#xA;Where I&#39;m at right now is on the other side (or very nearly on the other side)&#xA;of a choice I didn&#39;t feel freedom to make because it came from a conviction.&#xA;&#xA;The manager of the group I&#39;m in told me a story about working for another&#xA;mining company, where he was managing mines in the Congo. These mines can be in&#xA;the most remote of remote places on earth - in cultures where human life is&#xA;definitely treated differently than in the first-world midwest USA. Operators of&#xA;these huge mining trucks in these parts of the world can be smoking meth in the&#xA;cabs - which obviously leads to unsafe operation. Safety of the mines&#xA;themselves can be a second-thought as well, they collapse and then the people&#xA;inside have to be excavated out. Sean told me he had come back to work after a&#xA;weekend just to be told, as if it was no big deal, that &#34;Motombu died&#34; and to&#xA;find another operator. Very crass attitude towards human life...&#xA;&#xA;I&#39;m still pretty frustrated at Caterpillar for how they&#39;ve handled RTO (and&#xA;corporitisms like &#34;Caterpillar family&#34; and &#34;we bleed Cat yellow&#34; are&#xA;distracting nonsense), and part of me wants to leave still over the personal&#xA;offence, but Caterpillar will make the safest mining equipment, without a&#xA;doubt. And given that Cat will make the safest equipment then the story I was told and&#xA;the mission I couldn&#39;t help but believe in is that rolling out good autonomy&#xA;software and solutions to these remote mines will save lives.&#xA;&#xA;!!! warning &#34;&#34;&#xA;&#xA;    I&#39;m obviously not like superman or anything, but something feels better about enabling good autonomy rather than supporting an online sales platform, or working for a bank...&#xA;&#xA;I was invited (coerced?) into that mission - and my role in it is enabling the&#xA;Autonomy group to manage data for their software stack. I get to help these&#xA;engineers be experts in the things they&#39;re experts in by offloading some&#xA;operational overhead (data movement, pipelineing, infrastructure and devops,&#xA;etc.) so that they can produce the best autonomy software on the planet.&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Learning How To Agent</title>
    <id>https://pype.dev/learning-how-to-agent/</id>
    <updated>2026-02-03T05:50:33Z</updated>
    <published>2026-02-03T05:50:33Z</published>
    <link href="https://pype.dev/learning-how-to-agent/" rel="alternate" type="text/html"></link>
    <summary type="text">I&#39;ve been using AI tools for codegen for a few years now, but not super heavily. I either use the in-line copilot stuff, which is like LSP on seteroids, or I...</summary>
    <content type="text">&#xA;## Introduction &amp; Background&#xA;&#xA;I&#39;ve been using AI tools for codegen for a few years now, but not super&#xA;heavily. I either use the in-line copilot stuff, which is like LSP on&#xA;seteroids, or I have gone all the way to the other side of full on vibecoding&#xA;with Windsurf. That&#39;s been fun enough, but not super fulfilling and at the end&#xA;of that I either have a simple webapp that does what I want but I don&#39;t&#xA;understand, or else a half-broken thing I tried to understand but couldn&#39;t&#xA;prompt in the right direction.&#xA;&#xA;!!! note &#34;One Caveat&#34;&#xA;&#xA;    My one caveat with Windsurf, which is a full IDE that I don&#39;t have a lot of comfortable navigation in due to all the Cascade keybindings clobbaring my own keymaps - is that with a HEAVY spec, it&#39;s done will with the Claude models at implementing a real idea I have. It&#39;s closer to vibe-engineering than vibe-coding, but that&#39;s my only instance, the rest of my Windsurf usage is &#34;make me a cool app - no mistakes&#34;&#xA;&#xA;This year it feels like some tools exploded and&#xA;I mentioned this in [[new-job-caterpillar-autonomy]]. I&#39;ve been using&#xA;[opencode](https://opencode.ai) a lot over the last few weeks and have iterated&#xA;many times already on a system of work that I&#39;m trying to lean into for&#xA;improving my efficiency.&#xA;&#xA;## The Problem Space&#xA;&#xA;My desire for a great Developer Experience is years old now, shout out to&#xA;ThePrimeagen for his [FEM&#xA;course](https://frontendmasters.com/courses/developer-productivity-v2/) and&#xA;[Waylon Walker](https://waylonwalker.com) for being a constant source of&#xA;encouragement to be the best developer I can be. I sometimes (often) get&#xA;tunnel-visioned on developer-productivity initiatives and lose the forest&#xA;through the trees when ironing out a workflow - generally to find out I way&#xA;over complicated the solution OR worse, started solving a problem I don&#39;t even&#xA;have.&#xA;&#xA;## First Attempt: Local Progress Tracker&#xA;&#xA;Well that&#39;s where I&#39;ve been for a few weeks... I&#39;m building Nexus, my&#xA;second-brain at work to collaborate with agents on the truckload of stuff I&#39;m&#xA;expected to get done. For a while now I&#39;ve had a &#34;working-notes&#34; repo, which is&#xA;basically a blog, built with markata and navigated via markdown-lsp, where I do&#xA;like what I do here - take daily notes, track projects and status, and it gets&#xA;built into a nice little website I can reference with my boss.&#xA;&#xA;Now that we have copilot in full-swing, I&#39;m trying to integrate agents a bit&#xA;more. Opencode has made this so nice - so many tools and modes of interaction,&#xA;highly customizable interface but also an amazing default experience... So I&#xA;was trying to lean into using agents and subagents for more work I started down&#xA;the path of building out a progress tracker. I started with a simple &#34;skill&#34;&#xA;that told the agent to put some info into a sqlite file, and even create the&#xA;file and schema based on the work. This worked fine, but was specific to each&#xA;repo (and actually each worktree I was in) and it was hard for me to get&#xA;visibility into all the work my fleet of agents was doing. Now that&#39;s actually&#xA;problem 1 - I don&#39;t have a fleet of agents, I had some terminal sessions going&#xA;with opencode, but I got it in my head that I was going to have an army of&#xA;Claude&#39;s on my computer, constantly and autonomously knocking out tickets and I&#xA;needed to know who was doing what, where, when, and why..&#xA;&#xA;## Nexus V1 &amp; V2: The Over-Engineering Phase&#xA;&#xA;So, I dropped the local &#34;progress tracker&#34; and jumped into a huge FastAPI&#xA;project that I called Nexus. It was a python cli + api, with a server for&#xA;centralized management. It presented a kanban board, had policy gates on&#xA;&#34;plans&#34; being approved before work could start on an Epic (and therefore any&#xA;child tickets). It has worktree tracking and automation, etc. It had a lot...&#xA;it didn&#39;t all work, and it was hard to build the autonomous system... I wanted&#xA;agentic feedback loops where `voidshaper` and I made epic plans for Epics (see the pun?) and then once the Plan was approved `star-commander` comes on the scene and makes tickets or checks tickets, depending on what&#39;s already ready to go it farmed out the work to `starsmith` (and variants for complexity) to build and then automatically calls in `recon-officer` and `qa-engineer` and at the end of it `gatekeeper` came in and&#xA;approved or denied the changes. If denied - automatically start the loop again,&#xA;tracking the work in Nexus, if approved - rebase and merge the branch, clean up&#xA;the worktree, update the ticket, close it, and get working on the next thing&#xA;that opens up. Ticket dependencies were in there, tracking stale agent&#xA;sessions, clever routing of tasks to smaller models where appropriate.... you&#xA;can see that I went too far too fast too hard.&#xA;&#xA;![20260203124603_d5948740.png](https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20260203124603_d5948740.png)&#xA;&#xA;Nexus went through 2 or 3 iterations of this feature set. I was building it with several constraints in mind - specifically at work. 1. rate-limiting on large models (so not just using opus for everything), 2. good stewardship (not burning down cities for docstrings - farm out that work to haiku or a -mini model), 3. Copilot support - even though I like to use opencode, I wanted to supper copilot cli and in vscode as a means to share this with my coworkers who were mostly using vscode, 4. agent sessions were dying so I needed some kind of liveness probe for manual intervention in sessions where maybe the vpn died and the agent lost network... stuff like this was on my mind - but you know what wasn&#39;t? actually doing some work... I was solving problems I don&#39;t have, but were fun to think about.&#xA;&#xA;## The Reality Check&#xA;&#xA;So, what are my problems? I boiled them down to a few things...&#xA;&#xA;1. I want to manage my workstreams in workspaces - folders on my computer where I put git worktrees&#xA;2. I want varying levels of ai automation in my workflows... some things could be handled by an agent fully autonmously, but most things I&#39;m at least paired up, reviewing diffs still, doing research while working, etc.&#xA;3. In those varying levels of automation, I wanted the same amount of task tracking to a centralized location&#xA;&#xA;And after 2 weeks of ADHD-driven hyper focus, and many iterations on Nexus,&#xA;then Nexus V2.... what happened is that Nexus V2 died, and we say long live&#xA;Nexus V3!&#xA;&#xA;## Nexus V3: The Pragmatic Pivot&#xA;&#xA;Nexus V3 is the same idea, but different approach... I&#39;m leaning into opencode&#xA;tooling specifically, dropping my care to support copilot given the lack of&#xA;features. I&#39;m using a few opencode [plugins](https://opencode.ai/docs/plugins/)&#xA;([opencode-notify](https://github.com/kdcokenny/opencode-notify) and&#xA;[opencode-background-agents](https://github.com/kdcokenny/opencode-background-agents)&#xA;and a few [commands](https://opencode.ai/docs/commands/). Instead of building&#xA;my own tracker, I&#39;m using [kanboard](https://kanboard.org/) because it&#39;s&#xA;basically a feature-complete agile/sprint/kanban board that I use at home, has&#xA;a simple plugin ecosystem for light customization, and solves practically every&#xA;status-tracking problem I tried to build from the ground up initially - ticket&#xA;dependencies/linkages, actions to change ticket colors for a simple intuitive&#xA;UI based on state, easy columns and tagging configuration, a simple API and&#xA;there&#39;s even an [mcp server](https://github.com/bivex/kanboard-mcp).&#xA;&#xA;So, I&#39;ve given up on the full automation for now, although&#xA;[opencode-pilot](https://github.com/athal7/opencode-pilot) looks VERY PROMISING&#xA;for this in the future. Today though, through some simple commands to give to&#xA;agents for updating kanboard, I manually put them in worktrees, and they get to&#xA;work - the tracking and human-in-the-loop model is going well for the work I&#xA;need done.&#xA;&#xA;## Lessons Learned&#xA;&#xA;I learned and relearned plenty of lessons on this over the last 2 weeks... Data&#xA;models matter more than almost anything, well-defined workflows are required if&#xA;you want agents to help you iterate, and not everything has to be a product...&#xA;That last one&#39;s personal, but every time I have an idea I **think** is good,&#xA;I&#39;m sure it&#39;ll be something to share, but a good lesson for me is to just build&#xA;the things I need for me, and **eventually** maybe it can be cleaned up to&#xA;share, but when I start building something with anyone other than **me** in&#xA;mind, I&#39;m in for a long hard journey&#xA;&#xA;## Current State &amp; Future&#xA;&#xA;So what&#39;s the summary? I don&#39;t think I know how to agent super well yet - but&#xA;I&#39;m trying to get better to stay on the forefront of my co-workers who I see&#xA;using AI in simple and sometimes scary ways&#xA;&#xA;!!! danger &#34;Copilot x sudo&#34;&#xA;&#xA;    Do not give copilot `sudo` on your CI server and say &#34;fix my problem&#34;... are you retarded???&#xA;&#xA;I am not going hardcore with [ralph&#xA;wiggum](https://awesomeclaude.ai/ralph-wiggum) or&#xA;[gastown](https://github.com/steveyegge/gastown) - although those inspired&#xA;Nexus v1 and v2, but I am dialing in my agentic workflow with some simple&#xA;specialized agents, farming out work to subagents for context management,&#xA;planning ahead of time to put appropriate context in a ticket, and getting&#xA;close to having agents check out tickets, make worktrees, and do simple work by&#xA;themselves (this was working in Nexus V2 but only intermittenly).&#xA;&#xA;Someday I&#39;ll open-source Nexus and share the configuration and workflow, for&#xA;now it&#39;s private as I&#39;m actually using it to build out what I want rather than&#xA;trying to recreate gastown with a cool space theme.&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>New Job - Caterpillar Autonomy</title>
    <id>https://pype.dev/new-job-caterpillar-autonomy/</id>
    <updated>2026-01-26T10:24:54Z</updated>
    <published>2026-01-26T10:24:54Z</published>
    <link href="https://pype.dev/new-job-caterpillar-autonomy/" rel="alternate" type="text/html"></link>
    <summary type="text">In im-back-from-the-dead I mentioned my new role that started this year - it&#39;s a return to Caterpillar Autonomy. I built some data pipelines and junior-grade...</summary>
    <content type="text">&#xA;In [[im-back-from-the-dead]] I mentioned my new role that started this year -&#xA;it&#39;s a return to Caterpillar Autonomy. I built some data pipelines and&#xA;junior-grade infrastructure 5 years ago but left over burn-out at the prospect of&#xA;a job with smaller scoped work and more technical guidance. That was a good&#xA;move, and in God&#39;s sovereignty he&#39;s brought me back. Now I&#39;m looking at code,&#xA;thankfully not that I wrote, but that the guy after me wrote - it accomplished&#xA;a job, like my original work did, but also like what I built back then -&#xA;there&#39;s better ways today. The amount of things in front of me is absolutely&#xA;daunting, and the people on the team have been seeded with a very high opinion&#xA;of me... I don&#39;t know how much is valid and how much is hype, but I&#39;m gonna try&#xA;to live up to it.&#xA;&#xA;One way I&#39;m striving to do that is to maximize my efficiency with the AI coding&#xA;tools available to me. We have Github Copilot and I&#39;ve picked up a lot over the&#xA;course of the last few years (and especially more recently) in using agents,&#xA;planning out work, documenting plans for agents to follow, splitting up work&#xA;via worktrees, etc. I feel pretty good about where I&#39;m going and someday I&#xA;might even release Nexus. Nexus is serving as my second-brain at work, the hub&#xA;where I collaborate with my fleet of agents on the work that must get done.&#xA;Details to come on this, but the thing that matters is that in ironing out&#xA;workflow I&#39;ve moved from prompt + chat to really managing a long-lived stream&#xA;of work. AI has been changing the world, and there&#39;s been developer hype for&#xA;years now. But &#34;make me a cool app, no mistakes&#34; isn&#39;t going to cut it. Even&#xA;&#34;make an app but generate a plan first&#34; isn&#39;t going to cut it... Developers&#xA;have to adopt a higher level role, a systems-oriented and architecture-driven&#xA;worldview must become primary in order to keep up. Code-gen and syntax writing&#xA;are not what developers were **ever** paid to do, although many believed so.&#xA;Our jobs have been to solve problems and deliver code that solves the problem.&#xA;The code is cheap now, but solving problems still is a human task.&#xA;&#xA;In the Autonomy group - there&#39;s problems all up and down the stack. My focus is&#xA;on infrastructure and developer operations - it&#39;s become my bread and butter&#xA;over the last several years. I&#39;m excited to help the team grow, and I&#39;m excited&#xA;to grow personally/technically as I lean into the agentic workflow to produce&#xA;code that I&#39;m actually proud of, that has my name on the commit, and that&#xA;solves real problems.&#xA;&#xA;## Example - Local Development&#xA;&#xA;One of the first things I&#39;m tackling is a developer-pain-point of working on&#xA;their laptop. I&#39;ve been in this space for years, mostly with python programmer&#xA;who are writing data science code. They don&#39;t know about virtual environments,&#xA;checking $PATH, assuming bad state in their terminal session, how to configure&#xA;VS Code, etc. Often they just want to write some scripts and somehow test it.&#xA;The solution I see most often is for devs to write code in JupyterLab/Notebooks&#xA;in AWS or some environment close to their data - this is fine I guess, but it&#39;s&#xA;not developing good pipelines, and it&#39;s tedious as hell. In my last job I&#xA;helped set developers up with workflows that allowed them to run their IDE of&#xA;choice locally (getting all the goodies of syntax highlighting, LSP, etc) and a&#xA;CLI that took their code and ran it in the cloud, right next to data, in the&#xA;same way that prod runs. It was a hit. After that I introduced some tools to&#xA;help them manage python environments - we had strict templating requirements in&#xA;our projects, so making tools to automate those things wasn&#39;t too hard - it&#39;s&#xA;much easier than trying to make something flexible for every use case. The&#xA;opinions made the automation and tooling easy to make and distribute.&#xA;&#xA;Well I&#39;m up against a similar task now, but oh so much worse... Larger team,&#xA;larger environment sprawl, larger infrastructure mismanagement, the whole&#xA;gambit. And I&#39;m here for it... Here&#39;s the first problem I&#39;m addressing - local&#xA;development for Airflow DAGs that run in an Airflow deployment on Kubernetes.&#xA;The deployment itself is a little odd, Airflow is an orchestrator, all the&#xA;pipelines run in external AWS Batch jobs - so a DAG hits the Batch API to run&#xA;the code. The design there is actually nice, but how are devs testing code?&#xA;&#xA;Oh that&#39;s easy... they SSH into the prod server, which is a 5 year old desktop&#xA;THAT I BUILT WITH A CO-WORKER BEFORE I WAS IN AUTONOMY THE FIRST TIME... hold&#xA;on, WHAT!? Yes, it&#39;s true.. so they SSH into the prod server, run some bash&#xA;scripts in their userspace that setup airflow and a few db utilities in a&#xA;docker compose stack, authenticate with AWS themselves from that server, and&#xA;then they run DAGs against real data to test it... I am beyond shook.&#xA;&#xA;Here&#39;s what I&#39;ve put together - a bootstrap process (I like `just` + PEP 723&#xA;python scripts as opposed to bash, but to each their own, and bash of course&#xA;has its place) that spins up a [kind](https://kind.sigs.k8s.io/) cluster on&#xA;their laptop, the process pulls some private images and loads them into the&#xA;kind cluster, it installs airflow from a helm chart (the same helm chart we&#39;ll&#xA;use in dev and prod... no more docker compose over here, kubectl over there),&#xA;and everything just. comes. up. No SSH into ancient server, no touching cloud&#xA;infra (they get MinIO and a DB container to emulate S3 and RDS in our AWS&#xA;accounts), no sweat on testing DAGs. They stage some data in MinIO (`just open&#xA;minio` handles the port-forward and opens the browser), then `just open&#xA;airflow` (their DAGs are hot-reloaded via hostPath mounting), and they can run&#xA;DAGs locally until they&#39;re satisfied with the results.&#xA;&#xA;It&#39;s taken me about a week of split-focused effort (I mentioned Nexus and I&#39;ve&#xA;been co-building and dogfooding that at the same time) but I&#39;m proud of that&#xA;local setup now. I am a bit shocked they&#39;ve dealt with a brittle, hacky, often&#xA;broken development workflow for the last 4 years or so, but that development I&#xA;suppose.&#xA;&#xA;## The Point&#xA;&#xA;This post wasn&#39;t meant to be me glazing myself for awesome local development&#xA;practices, I am simply excited about this new chapter. I love solving problems,&#xA;and I love owning those solutions. There&#39;s a whole mess of things to address,&#xA;my mind is buzzing, and I feel like God has blessed me with renewed passion&#xA;(again see [[im-back-from-the-dead]]). I&#39;ve given up some pay and some freedom&#xA;to take this role, but I think it&#39;ll pay dividends.&#xA;&#xA;Life happens to all of us - this role change affected a lot for me, it&#39;s been&#xA;hard to digest some of those changes, but the work is good, the development is&#xA;fun, the new world of using agents to fly through things you&#39;re fluent in is&#xA;exciting, and I&#39;m here to help a team that desperately needs it to improve&#xA;their lives and the work we do for Cat Autonomy.&#xA;&#xA;I&#39;m still pissed at Caterpillar Executives for RTO ruining parts of my life,&#xA;but in God&#39;s sovereignty their idiocy has led to non-trivial blessing, so I can&#xA;say &#34;Praise the Lord&#34;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Increase inotify limit in your CI workers</title>
    <id>https://pype.dev/increase-inotify-limit-in-your-ci-workers/</id>
    <updated>2025-12-30T06:11:49Z</updated>
    <published>2025-12-30T06:11:49Z</published>
    <link href="https://pype.dev/increase-inotify-limit-in-your-ci-workers/" rel="alternate" type="text/html"></link>
    <summary type="text">Today I tripped over a CI failure that I had to think about for a while.</summary>
    <content type="html">&lt;p&gt;Today I tripped over a CI failure that I had to think about for a while.&lt;/p&gt;&#xA;&lt;p&gt;I build &lt;a href=&#34;https://github.com/zensical/zensical&#34;&gt;zensical&lt;/a&gt; static sites in CI on&#xA;my Forgejo instance. These builds had been working fine, then suddenly started&#xA;failing with no code changes. Naturally, I assumed something upstream broke —&#xA;maybe a new uv release, maybe zensical.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I pinned versions.&lt;/li&gt;&#xA;&lt;li&gt;I tested older versions.&lt;/li&gt;&#xA;&lt;li&gt;Same failure every time.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That ruled out regressions and pushed me toward the environment.&lt;/p&gt;&#xA;&lt;p&gt;I pulled the runner and worker images locally and built the sites just fine&amp;hellip;&#xA;But that doesn&amp;rsquo;t perfectly emulate the CI setup - my forgejo runner relies on&#xA;docker-in-docker and so we aren&amp;rsquo;t &lt;strong&gt;just&lt;/strong&gt; running a container on a host, we have&#xA;this middle layer to consider&amp;hellip; I wasn&amp;rsquo;t sure how to really test this out&#xA;locally so I succomed to AI and here&amp;rsquo;s where Jipity got me in about 5&#xA;minutes&amp;hellip;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-failure&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The failure&lt;/span&gt; &lt;a href=&#34;#the-failure&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The builds blew up with:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;thread &#39;zrx/monitor&#39; panicked at .../zensical-watch/src/agent/monitor.rs:154:49:&#xA;called `Result::unwrap()` on an `Err` value:&#xA;Error { kind: Io(Os { code: 24, message: &amp;quot;Too many open files&amp;quot; }) }&#xA;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;At first glance it means nothing to me but Jipity says this screams ulimit.&lt;/p&gt;&#xA;&lt;p&gt;So following the AI overlords I checked:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;ulimit -n&lt;/code&gt; was already very high&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;/proc/sys/fs/inotify/max_user_watches&lt;/code&gt; was also very high&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;PID limits&lt;/code&gt; were not constrained&lt;/p&gt;&#xA;&lt;p&gt;Everything looked fine according to Jipity.&lt;/p&gt;&#xA;&lt;p&gt;Yet the panic persisted.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-real-culprit&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The real culprit&lt;/span&gt; &lt;a href=&#34;#the-real-culprit&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The actual limit being hit was:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;/proc/sys/fs/inotify/max_user_instances&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;In my Forgejo runner container, it was set to 128.&lt;/p&gt;&#xA;&lt;p&gt;That turns out to be far too low for zensical.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s what ChatGPT said:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Even during a normal zensical build, the tool spins up its watch subsystem,&#xA;which creates many inotify instances. Once it crosses the kernel limit,&#xA;inotify_init() fails with EMFILE, and the process panics because the error is&#xA;unwrapped.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;the-fix&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;The fix&lt;/span&gt; &lt;a href=&#34;#the-fix&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Raising the inotify instance limit fixed it immediately:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;echo 1024 &amp;gt; /proc/sys/fs/inotify/max_user_instances&#xA;RUST_BACKTRACE=full uvx zensical build --clean&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;After that, the build succeeded consistently.&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Prettier Docker ‘ps’ Command</title>
    <id>https://pype.dev/prettier-docker-ps-command/</id>
    <updated>2025-12-29T05:33:05Z</updated>
    <published>2025-12-29T05:33:05Z</published>
    <link href="https://pype.dev/prettier-docker-ps-command/" rel="alternate" type="text/html"></link>
    <summary type="text">The command is very useful, but I hate reading the output. Turns out, you can make it prettier:</summary>
    <content type="html">&lt;p&gt;The &lt;code&gt;docker ps&lt;/code&gt; command is very useful, but I hate reading the output. Turns&#xA;out, you can make it prettier:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&amp;ndash;format &amp;ldquo;table&amp;rdquo; is implied with the command.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;pre&gt;&lt;code&gt;&#xA;❯ docker ps --format &amp;quot;table&amp;quot; | grep can&#xA;9b716a7d1ab0   postgres:17                                               &amp;quot;docker-entrypoint.s…&amp;quot;   13 hours ago    Up 13 hours (healthy)            0.0.0.0:5432-&amp;gt;5432/tcp, [::]:5432-&amp;gt;5432/tcp                                                cannalyzer-db-1&#xA;f7708ea0c112   adminer                                                   &amp;quot;entrypoint.sh docke…&amp;quot;   13 hours ago    Up 13 hours                      0.0.0.0:8081-&amp;gt;8080/tcp, [::]:8081-&amp;gt;8080/tcp                                                cannalyzer-adminer-1&#xA;93cd67719ed4   frontend:latest                                           &amp;quot;/docker-entrypoint.…&amp;quot;   13 hours ago    Up 13 hours                      0.0.0.0:5173-&amp;gt;80/tcp, [::]:5173-&amp;gt;80/tcp                                                    cannalyzer-frontend-1&#xA;f517625fca98   traefik:3.0                                               &amp;quot;/entrypoint.sh --pr…&amp;quot;   13 hours ago    Up 13 hours                      0.0.0.0:80-&amp;gt;80/tcp, [::]:80-&amp;gt;80/tcp, 0.0.0.0:8090-&amp;gt;8080/tcp, [::]:8090-&amp;gt;8080/tcp           cannalyzer-proxy-1&#xA;f4daa036216e   schickling/mailcatcher                                    &amp;quot;sh -c &#39;mailcatcher …&amp;quot;   13 hours ago    Up 13 hours                      0.0.0.0:1025-&amp;gt;1025/tcp, [::]:1025-&amp;gt;1025/tcp, 0.0.0.0:1080-&amp;gt;1080/tcp, [::]:1080-&amp;gt;1080/tcp   cannalyzer-mailcatcher-1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;But you can pass a template string to the &lt;code&gt;--format&lt;/code&gt; option, like so:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;docker ps --format &amp;quot;table {{.Names}}&amp;quot;&lt;/code&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&#xA;✗ docker ps --format &amp;quot;table {{.Names}}&amp;quot; | grep can&#xA;cannalyzer-db-1&#xA;cannalyzer-adminer-1&#xA;cannalyzer-frontend-1&#xA;cannalyzer-proxy-1&#xA;cannalyzer-mailcatcher-1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;docker ps --format &amp;quot;table {{.Names}}\t{{.Status}}\t{{.Ports}}&amp;quot;&lt;/code&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&#xA;❯ docker ps --format &amp;quot;table {{.Names}}\t{{.Ports}}&amp;quot; | grep can&#xA;cannalyzer-db-1             0.0.0.0:5432-&amp;gt;5432/tcp, [::]:5432-&amp;gt;5432/tcp&#xA;cannalyzer-adminer-1        0.0.0.0:8081-&amp;gt;8080/tcp, [::]:8081-&amp;gt;8080/tcp&#xA;cannalyzer-frontend-1       0.0.0.0:5173-&amp;gt;80/tcp, [::]:5173-&amp;gt;80/tcp&#xA;cannalyzer-proxy-1          0.0.0.0:80-&amp;gt;80/tcp, [::]:80-&amp;gt;80/tcp, 0.0.0.0:8090-&amp;gt;8080/tcp, [::]:8090-&amp;gt;8080/tcp&#xA;cannalyzer-mailcatcher-1    0.0.0.0:1025-&amp;gt;1025/tcp, [::]:1025-&amp;gt;1025/tcp, 0.0.0.0:1080-&amp;gt;1080/tcp, [::]:1080-&amp;gt;1080/tcp&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;picture&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;Picture&lt;/span&gt; &lt;a href=&#34;#picture&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;I noticed my template isn&amp;rsquo;t folding the codeblocks in a way that actually makes this post look like I&amp;rsquo;m lying!&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251229122717_67e76a67.png&#34; alt=&#34;20251229122717_67e76a67.png&#34;&gt;&#xA;&lt;/figure&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
  <entry>
    <title>Setup A Cloudflare Tunnel With Terraform</title>
    <id>https://pype.dev/setup-a-cloudflare-tunnel-with-terraform/</id>
    <updated>2025-12-12T21:06:59Z</updated>
    <published>2025-12-12T21:06:59Z</published>
    <link href="https://pype.dev/setup-a-cloudflare-tunnel-with-terraform/" rel="alternate" type="text/html"></link>
    <summary type="text">I am cooking up some stuff at home and want to put it on the interwebs, but I don&#39;t want it on the same infra as my homelab. Now... I only have a server or...</summary>
    <content type="html">&lt;p&gt;I am cooking up some stuff at home and want to put it on the interwebs, but I&#xA;don&amp;rsquo;t want it on the same infra as my homelab. Now&amp;hellip; I only have a server or&#xA;2, so to some degree it will be, but networking-wise I didn&amp;rsquo;t want to funnel&#xA;extra traffic through my reverse proxy.&lt;/p&gt;&#xA;&lt;p&gt;So, I&amp;rsquo;d heard about Cloudflare Tunnels - they sound like P2P VPN to me, but I&#xA;know there&amp;rsquo;s layers of the networking stack I&amp;rsquo;m blatantly ignoring. &amp;ldquo;What the&#xA;tunnel is&amp;rdquo; isn&amp;rsquo;t much the point - I&amp;rsquo;m here to show you how to set one up and&#xA;get yourself a fancy &lt;a href=&#34;https://app.mydomain.com&#34;&gt;https://app.mydomain.com&lt;/a&gt; for your web app running&#xA;kind of wherever you want&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Example Repo linked at the bottom&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;requirements&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;Requirements&lt;/span&gt; &lt;a href=&#34;#requirements&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ol start=&#34;0&#34;&gt;&#xA;&lt;li&gt;Terraform or open-tofu. I currently use open-tofu but either would be fine.&#xA;&lt;code&gt;brew install open-tofu&lt;/code&gt; is a simple way to get going&lt;/li&gt;&#xA;&lt;li&gt;Cloudflare account with a domain&lt;/li&gt;&#xA;&lt;li&gt;API token with permissions:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Account:Cloudflare Tunnel:Edit&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Zone:DNS:Edit&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;cloudflared&lt;/code&gt; (the example repo runs cloudflared in a docker compose stack)&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;tunnel&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;Tunnel&lt;/span&gt; &lt;a href=&#34;#tunnel&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;The module is simple and has just a few resources:&lt;/p&gt;&#xA;&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;❯ tofu state list&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;module.tunnel.cloudflare_record.tunnel&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;module.tunnel.cloudflare_tunnel_config.this&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;module.tunnel.cloudflare_zero_trust_tunnel_cloudflared.this&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;module.tunnel.random_id.tunnel_secret&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We see there will be the a DNS record that tofu references by the key &amp;ldquo;tunnel&amp;rdquo;.&#xA;There is a tunnel configuration resource, the tunnel resource itself, and&#xA;finally the associated secret required for the cloudflared daemon that will run&#xA;alongside your webapp.&lt;/p&gt;&#xA;&lt;p&gt;To get started you&amp;rsquo;ll need to fill out the example &lt;code&gt;terraform.tfvars&lt;/code&gt; file with&#xA;your info:&lt;/p&gt;&#xA;&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Copy to terraform.tfvars and fill in values&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# DO NOT commit terraform.tfvars to git&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;cloudflare_api_token&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your-api-token-here&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;cloudflare_account_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your-account-id&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;cloudflare_zone_id&lt;/span&gt;    &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your-zone-id&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;domain&lt;/span&gt;                &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;subdomain&lt;/span&gt;             &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;tunnel_name&lt;/span&gt;           &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;my-tunnel&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;origin_service&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://localhost:8000&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can grab your account id and zone id from Cloudflare&amp;rsquo;s dashboard for your&#xA;domain. It&amp;rsquo;s near the bottom of the Overview page&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251213113332_0e0f09b8.png&#34; alt=&#34;20251213113332_0e0f09b8.png&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;Then I presume you have a domain already, but if not hop over to namecheap to&#xA;snag one and then register it with cloudflare so they can manage your DNS. I&#xA;have terraform for this as well, a future blog post will combine this with a&#xA;fuller terraform&amp;rsquo;d cloudflare setup for simple domain use cases&lt;/p&gt;&#xA;&lt;p&gt;Once you fill those out, hit it with the &lt;code&gt;tofu init&lt;/code&gt; and &lt;code&gt;tofu plan&lt;/code&gt; to see what&amp;rsquo;s up&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;NOTE: &lt;code&gt;terraform.tfvars&lt;/code&gt; is automatically sourced by terraform/tofu, you can name the file differently and then pass &lt;code&gt;-var-file=myvars.tfvars&lt;/code&gt; to the commands&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The initial plan should look something like this:&lt;/p&gt;&#xA;&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  + create&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;OpenTofu will perform the following actions:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# module.tunnel.cloudflare_record.tunnel will be created&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  + resource &lt;span class=&#34;s2&#34;&gt;&amp;#34;cloudflare_record&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tunnel&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;allow_overwrite&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;comment&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Managed by Terraform - soonish-tunnel&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;content&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;created_on&lt;/span&gt;      &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;hostname&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;id&lt;/span&gt;              &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;metadata&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;modified_on&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;            &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;proxiable&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;proxied&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;             &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nb&#34;&gt;type&lt;/span&gt;            &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CNAME&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;value&lt;/span&gt;           &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;zone_id&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;REDACTED&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# module.tunnel.cloudflare_tunnel_config.this will be created&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  + resource &lt;span class=&#34;s2&#34;&gt;&amp;#34;cloudflare_tunnel_config&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;this&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;account_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;REDACTED&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;id&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;tunnel_id&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + config &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          + ingress_rule &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              + &lt;span class=&#34;nv&#34;&gt;hostname&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;app.notifiq.net&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              + &lt;span class=&#34;nv&#34;&gt;service&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://localhost:8000&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          + ingress_rule &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;              + &lt;span class=&#34;nv&#34;&gt;service&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;http_status:404&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# module.tunnel.cloudflare_zero_trust_tunnel_cloudflared.this will be created&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  + resource &lt;span class=&#34;s2&#34;&gt;&amp;#34;cloudflare_zero_trust_tunnel_cloudflared&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;this&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;account_id&lt;/span&gt;   &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;REDACTED&amp;gt;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;cname&lt;/span&gt;        &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;id&lt;/span&gt;           &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;name&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;soonish-tunnel&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;secret&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;sensitive value&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;tunnel_token&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;sensitive value&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;c1&#34;&gt;# module.tunnel.random_id.tunnel_secret will be created&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  + resource &lt;span class=&#34;s2&#34;&gt;&amp;#34;random_id&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tunnel_secret&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;b64_std&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;b64_url&lt;/span&gt;     &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;byte_length&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;32&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;dec&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;hex&lt;/span&gt;         &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      + &lt;span class=&#34;nv&#34;&gt;id&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;known after apply&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Plan: &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; to add, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; to change, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; to destroy.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As long as that looks good you to, then we &lt;code&gt;tofu apply&lt;/code&gt; next (type &lt;code&gt;yes&lt;/code&gt; when&#xA;asked or pass &lt;code&gt;-auto-approve&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;p&gt;Afterwards &lt;code&gt;tofu state list&lt;/code&gt; should show you the 4 resources, and if you go to&#xA;your cloudflare zone&amp;rsquo;s dashboard you should see the CNAME associated with the&#xA;tunnel address&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;&lt;img src=&#34;https://cdn.statically.io/gh/pypeaday/images.pype.dev/main/blog-media/20251213120004_d199e5fd.png&#34; alt=&#34;20251213120004_d199e5fd.png&#34;&gt;&#xA;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;daemon&#34;&gt;&lt;span class=&#34;heading-wear-glyph&#34;&gt;Daemon&lt;/span&gt; &lt;a href=&#34;#daemon&#34; class=&#34;heading-anchor&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Run the compose stack or the binary itself. Get the token from terraform state with &lt;code&gt;tofu output -raw tunnel_token&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;TUNNEL_TOKEN=$(tofu output -raw tunnel_token) docker compose up -d&lt;/code&gt; will do you nicely&lt;/p&gt;&#xA;&lt;p&gt;Enjoy your tunnel!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/pypeaday/example-terraform-cloudflare-tunnel&#34;&gt;example repo&lt;/a&gt;&lt;/p&gt;&#xA;</content>
    <author>
      <name>Nic Payne</name>
      <uri>https://pype.dev</uri>
    </author>
  </entry>
</feed>