<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>T KOLSTO · Tagged sidequest</title><link>https://t.kolsto.no/tags/sidequest/</link><description>Things I built because I wanted them to exist. No roadmap, no company, usually no cloud.</description><generator>Hugo</generator><language>en</language><copyright>© 2010 - 2026 Thomas Kolstø</copyright><lastBuildDate>Sun, 04 Oct 2026 05:15:00 +0000</lastBuildDate><atom:link href="https://t.kolsto.no/tags/sidequest/" rel="self" type="application/rss+xml"/><image><url>https://t.kolsto.no/img/avatar.jpg</url><title>T KOLSTO · Tagged sidequest</title><link>https://t.kolsto.no/tags/sidequest/</link></image><item><title>Eight Arms, All Billable</title><link>https://t.kolsto.no/blog/clocktopus/</link><pubDate>Sun, 04 Oct 2026 05:15:00 +0000</pubDate><guid>https://t.kolsto.no/blog/clocktopus/</guid><dc:creator>Thomas Kolstø</dc:creator><category>clocktopus</category><category>macos</category><category>swift</category><category>sidequest</category><category>time-tracking</category><description>Clocktopus is a local macOS time tracker that suggests work sessions and asks before recording them. No screenshots, no cloud. Suggest, never surveil.</description><enclosure url="https://t.kolsto.no/img/clocktopus-share-v1.png" length="51601" type="image/png"/><content:encoded>&lt;p&gt;I used to perform a ritual every Friday that I suspect you&amp;rsquo;ll recognize. Timesheets were due in xledger—hours split across projects and activities because my time belonged to multiple budgets. I would fill them out with the confidence of someone who had been present all week, only to realize I couldn&amp;rsquo;t recall what I&amp;rsquo;d actually done.&lt;/p&gt;&#10;&lt;p&gt;So, I reconstructed it: running &lt;code&gt;git log --since=monday&lt;/code&gt; across repos, checking calendars for meetings, and scrolling Slack history to account for missing time. Then I translated this archaeology into tidy quarter-hour blocks and submitted it as fact.&lt;/p&gt;&#10;&lt;p&gt;Memory is the worst database. It lacks a write-ahead log, runs compaction nightly whether you like it or not, and every query returns a plausible answer with no indication of its truthfulness.&lt;/p&gt;&#10;&lt;h2 id="the-tools-are-all-wrong"&gt;The tools are all wrong&lt;/h2&gt;&#10;&lt;p&gt;The obvious fix is a timer: start when working, stop when done, making Friday an export button. But manual trackers fail because they require &lt;em&gt;remembering&lt;/em&gt; to use them. I don&amp;rsquo;t forget how to work; I forget to say that I am working. Any tool relying on me forming a new habit at the precise moment my brain is busy with something else is designed to fail. My collection of abandoned timewarrior databases proves this.&lt;/p&gt;&#10;&lt;p&gt;At the other end are &amp;ldquo;automatic&amp;rdquo; trackers. They solve forgetting by removing you from the loop entirely. They screenshot your screen every thirty seconds, run it through AI, and upload &amp;ldquo;productivity insights&amp;rdquo; to a cloud dashboard—often read by your manager. I work with patient data for a telemedicine company. A tool that photographs my display and posts it to someone else&amp;rsquo;s cloud isn&amp;rsquo;t an aid; it&amp;rsquo;s an incident report with a subscription fee.&lt;/p&gt;&#10;&lt;p&gt;This leaves a gap: one end trusts you to remember, the other doesn&amp;rsquo;t trust you at all.&lt;/p&gt;&#10;&lt;h2 id="the-sidequest"&gt;The sidequest&lt;/h2&gt;&#10;&lt;p&gt;So, obviously, I built one. This is a proper sidequest: no cloud, no accounts, no roadmap committee, and no ambitions of becoming a company. Just the tracker I wanted, built how any tool touching my work machine should be.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s called &lt;a href="https://github.com/tkolsto/clocktopus"&gt;Clocktopus&lt;/a&gt;. Eight arms, all billable.&lt;/p&gt;&#10;&lt;img src="https://t.kolsto.no/img/clocktopus-popover.webp" alt="Clocktopus menubar popover with a running timer, project list, and weekly activity chart" width="600" height="764" loading="lazy"&gt;&#10;&lt;p&gt;The insight is embarrassingly simple: your machine already knows what you&amp;rsquo;re working on. Not via AI—but via &lt;code&gt;pwd&lt;/code&gt;. My shells are cd&amp;rsquo;d into a client&amp;rsquo;s repo. My tmux panes sit in project directories. A running process has a working directory that names the project outright. The frontmost window title shows the open codebase. None of this needs a vision model; it needs a scorer over deterministic signals and a config file where &lt;em&gt;you&lt;/em&gt; write the rules:&lt;/p&gt;&#10;&lt;div class="tk-code"&gt;&lt;div class="tk-code__tools"&gt;&lt;span class="tk-code__lang"&gt;toml&lt;/span&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Initech&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;xledger_project&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;T200001&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;xledger_activity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DEV&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;dirs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;~/src/initech*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# shell / tmux / AI-agent cwds&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;initech&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c"&gt;# frontmost window title&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Longest prefix wins, so &lt;code&gt;~/src/initech-api&lt;/code&gt; beats your &lt;code&gt;~/src&lt;/code&gt; catch-all. Every rule is yours, in a version-controllable TOML file, and every guess shows exactly which signals fired. No vibes.&lt;/p&gt;&#10;&lt;h2 id="suggest-never-surveil"&gt;Suggest, never surveil&lt;/h2&gt;&#10;&lt;p&gt;This is the core feature. When a project holds my focus for minutes, Clocktopus doesn&amp;rsquo;t clock me in. It &lt;em&gt;asks&lt;/em&gt;. A dashed ghost block appears on the day timeline—&amp;ldquo;is this Initech?&amp;quot;—with evidence attached. One click confirms it, backfilling to when I started (the whole point: the app remembers the start time I was too busy to announce). One click dismisses it. Nothing lands in the database without my say-so.&lt;/p&gt;&#10;&lt;img src="https://t.kolsto.no/img/clocktopus-day-timeline.webp" alt="Clocktopus day timeline with a dashed ghost block suggesting a detected work session" width="1800" height="1800" srcset="https://t.kolsto.no/img/clocktopus-day-timeline_hu_6a68ec9bc18832fe.webp 720w, https://t.kolsto.no/img/clocktopus-day-timeline_hu_228a98bf6b529b21.webp 1440w, https://t.kolsto.no/img/clocktopus-day-timeline.webp 1800w" sizes="(min-width: 1120px) 1040px, (min-width: 601px) calc(100vw - 80px), calc(100vw - 40px)" loading="eager" fetchpriority="high"&gt;&#10;&lt;p&gt;The surveillance question is settled by architecture, not policy: the app makes zero network requests. Not for sync, telemetry, or updates—grep the source. Your hours are one SQLite file in &lt;code&gt;~/Library/Application Support&lt;/code&gt;, and the only thing leaving the machine is the CSV you export yourself.&lt;/p&gt;&#10;&lt;h2 id="friday-after"&gt;Friday, after&lt;/h2&gt;&#10;&lt;p&gt;The ritual now: open the week grid (ISO weeks, Monday start), with per-project totals already rounded to the billing increment. I skim it, nudge a block edge if the octopus and I disagree about lunch, and export. The CSV speaks xledger&amp;rsquo;s upload dialect—semicolon-separated, &lt;code&gt;yyyymmdd&lt;/code&gt; dates, decimal hours—because that&amp;rsquo;s what my Fridays demanded, but it&amp;rsquo;s just CSV; point it at whatever your accounting department worships.&lt;/p&gt;&#10;&lt;img src="https://t.kolsto.no/img/clocktopus-week-grid.webp" alt="Clocktopus week review with per-project hours, separate billable and personal totals, and CSV export" width="1800" height="1380" srcset="https://t.kolsto.no/img/clocktopus-week-grid_hu_e107ec88c1b571e0.webp 720w, https://t.kolsto.no/img/clocktopus-week-grid_hu_2e807c2023dde021.webp 1440w, https://t.kolsto.no/img/clocktopus-week-grid.webp 1800w" sizes="(min-width: 1120px) 1040px, (min-width: 601px) calc(100vw - 80px), calc(100vw - 40px)" loading="lazy"&gt;&#10;&lt;p&gt;The archaeology is gone. That&amp;rsquo;s the entire return on investment, and it pays out weekly.&lt;/p&gt;&#10;&lt;h2 id="questions-youre-already-typing"&gt;Questions you&amp;rsquo;re already typing&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;&amp;ldquo;Why not ActivityWatch?&amp;rdquo;&lt;/strong&gt; A genuinely good project for passive analytics—it lets you study yourself afterwards. Clocktopus is suggest-and-confirm for &lt;em&gt;billing&lt;/em&gt;: nothing is recorded until you approve it, and the output is a timesheet, not a chart. And timewarrior is fine, but it still trusts me to remember. We&amp;rsquo;ve established how that goes.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&amp;ldquo;Linux port?&amp;rdquo;&lt;/strong&gt; The core—domain models, scoring, rounding, export, ~90 tests—is a pure SwiftPM package with no AppKit, so it&amp;rsquo;s portable-ish. Signal collectors are the macOS-specific part. If cwd-sniffing on Linux sounds interesting, the repo is MIT.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&amp;ldquo;Why isn&amp;rsquo;t it notarized?&amp;rdquo;&lt;/strong&gt; An Apple Developer ID is $99/year, and this octopus is young. The release build is ad-hoc signed: right-click to open the first time, or build from source (Gatekeeper has no opinions).&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&amp;ldquo;Can a tmux pane I forgot about bill a client from three days ago?&amp;rdquo;&lt;/strong&gt; No. This was the heuristic I sweated most. Background signals—parked panes, long-running agent sessions—can only &lt;em&gt;reinforce&lt;/em&gt; a project that also has foreground evidence. A forgotten pane can&amp;rsquo;t clock you in on its own; it can only corroborate what you are demonstrably looking at.&lt;/p&gt;&#10;&lt;h2 id="coda"&gt;Coda&lt;/h2&gt;&#10;&lt;p&gt;Clocktopus is &lt;a href="https://github.com/tkolsto/clocktopus"&gt;on GitHub&lt;/a&gt;, MIT licensed, macOS 13+, Swift/SwiftUI, native menubar app. It has exactly one user in production, whose Fridays are much improved. If your accounting department also worships a CSV, maybe it&amp;rsquo;ll improve yours.&lt;/p&gt;&#10;</content:encoded></item><item><title>In other news</title><link>https://t.kolsto.no/blog/in-other-news/</link><pubDate>Tue, 11 Oct 2016 15:20:26 +0200</pubDate><guid>https://t.kolsto.no/blog/in-other-news/</guid><dc:creator>Thomas Kolstø</dc:creator><category>video</category><category>ffmpeg</category><category>sidequest</category><description>Serduno: a news channel aggregator, recorder and remixer I built as an ffmpeg playground. 15+ live channels, a week of backlog, 3x3 matrix view.</description><enclosure url="https://t.kolsto.no/img/serduno.png" length="54898" type="image/png"/><content:encoded>&lt;p&gt;I&amp;rsquo;ve been investing some of my spare time creating this news channel aggregator, recorder and (re-)mixer -thing lately, something that had been bubbling in the back of my mind since at least 2008.&lt;/p&gt;&#10;&lt;img src="https://t.kolsto.no/img/serduno.png" alt="Serduno home page: a retro television showing a three-by-three grid of live news channels, with a Jerry Seinfeld quote about the news always fitting the newspaper" width="731" height="515" srcset="https://t.kolsto.no/img/serduno_hu_e7d6c656dfc8ecd1.webp 720w, https://t.kolsto.no/img/serduno_hu_8add6925def3857e.webp 731w" sizes="(min-width: 1120px) 1040px, (min-width: 601px) calc(100vw - 80px), calc(100vw - 40px)" loading="eager" fetchpriority="high"&gt;&lt;p&gt;I also had to brush up on my ffmpeg-fu for some actual work related stuff, so this project ended up serving as a playground for doing both.&lt;/p&gt;&#10;&lt;p&gt;The start was an idea that it might be interesting to see how the different news outlets prioritises and covers (or not) the events that unfolds around the world. Then I figured it would also be nice to archive this for some period of time, so that if world war 3 were to break out while I&amp;rsquo;m walking the dogs, I can just check the instant replay (… or not.)&lt;/p&gt;&#10;&lt;p&gt;Way too many hours was spent looking into how to interpret the output of running encoders, and restarting them when needed; I&amp;rsquo;m relying on some rather unreliable/poorly connected sources for some of the channels. If anyone has any suggestions to how to improve this or have some private feeds to share I&amp;rsquo;m all ears.&lt;/p&gt;&#10;&lt;p&gt;There is a (limited) mechanism for &amp;ldquo;private&amp;rdquo; channels in which I&amp;rsquo;ve added a couple of commercial ones that I pay for just myself. I should probably do a writeup some day on how I ended up with OBS running in x11vnc doing screen-grabbing of firefox. It is nothing else than a hack, but as hacks go this one seems to work rather well.&lt;/p&gt;&#10;&lt;p&gt;&lt;del&gt;Try it out over at ser.du.no and please let me know what you think.&lt;/del&gt;&lt;/p&gt;&#10;&lt;p&gt;Features:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;15+ live news channels from around the world, most in English&lt;/li&gt;&#10;&lt;li&gt;7 days backlog segmented by channel/day for all channels&lt;/li&gt;&#10;&lt;li&gt;Rotate/view multiple channels simultaneous in 1x1 2x2 and 3x3 matrixes&lt;/li&gt;&#10;&lt;li&gt;Embedded chat with live-breaking-news-alert-bot v0.1&lt;/li&gt;&#10;&lt;li&gt;Loop video to make it easy to extract soundbites&lt;/li&gt;&#10;&lt;li&gt;Chromecast &amp;amp; Airplay support (on some of the streams)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Todo:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Chat/live-breaking-news-alert-bot needs serious work to provide less comedic relief. OCR is hard.&lt;/li&gt;&#10;&lt;li&gt;Re-enable DASH tech for video.js. Something broke with last version.&lt;/li&gt;&#10;&lt;li&gt;Make youtube-tech optional via selection, it&amp;rsquo;s not that great for matrixes (slow)&lt;/li&gt;&#10;&lt;li&gt;Create Recording/Export module for video.js&lt;/li&gt;&#10;&lt;li&gt;Set up nginx hls/dash secure links&lt;/li&gt;&#10;&lt;li&gt;Add moar channels. Linux/PC based sat recievers might be the way to go.&lt;/li&gt;&#10;&lt;li&gt;Make channel-matrixes refresh one by one instead of all of them at the same time somewhat fixed for 3x3&lt;/li&gt;&#10;&lt;li&gt;#hindenburg { float: none; }. I am not a designer. The lack of design + related issues will forever be banished to the end of this list, unless someone wants to help.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>WebGL Traffic Analyzer</title><link>https://t.kolsto.no/blog/webgl-traffic-analyzer/</link><pubDate>Sun, 09 Nov 2014 14:45:00 +0200</pubDate><guid>https://t.kolsto.no/blog/webgl-traffic-analyzer/</guid><dc:creator>Thomas Kolstø</dc:creator><category>webgl</category><category>html5</category><category>dataviz</category><category>sidequest</category><description>A weekend WebGL globe for the ops centre video wall, mixing IDS and firewall data into eye candy that only I could interpret.</description><enclosure url="https://t.kolsto.no/img/trafficanalyzer.jpg" length="27329" type="image/jpeg"/><content:encoded>&lt;p&gt;Last weekend I created a WebGL Traffic Globe Analyzer thing for our operations centre video wall.&lt;/p&gt;&#10;&lt;p&gt;Here it is.&lt;/p&gt;&#10;&lt;img src="https://t.kolsto.no/img/trafficanalyzer.jpg" alt="WebGL globe showing the Atlantic and Europe on a black background, with red and green spikes marking traffic sources and a clock in the corner" width="916" height="662" srcset="https://t.kolsto.no/img/trafficanalyzer_hu_29e91e3ec5c8f543.webp 720w, https://t.kolsto.no/img/trafficanalyzer_hu_cb944b1a5b04e08d.webp 916w" sizes="(min-width: 1120px) 1040px, (min-width: 601px) calc(100vw - 80px), calc(100vw - 40px)" loading="eager" fetchpriority="high"&gt;&lt;p&gt;Project is deeply inspired by a similar globe I once saw in a lobby somewhere in Silicon Valley, but that one was &amp;ldquo;just&amp;rdquo; displaying aggregate search traffic. I&amp;rsquo;ve tried to mix in some sensor data from our IDS system and firewalls to make it a bit more interesting to look at with our comparatively humble dataset.&lt;/p&gt;&#10;&lt;p&gt;It&amp;rsquo;s really more eye candy than useful, especially since only I know what the different colors represent (..but maybe you can guess some of them?).&lt;/p&gt;&#10;&lt;p&gt;You know what they say, there are only two kinds of people in this world — those who can extrapolate from incomplete data sets…&lt;/p&gt;&#10;</content:encoded></item></channel></rss>