<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>TIL.cafe</title>
    <subtitle>TIL.cafe - Brian Dorsey learning in public</subtitle>
    <link rel="self" type="application/atom+xml" href="https://TIL.cafe/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://TIL.cafe"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-01-01T00:00:00+00:00</updated>
    <id>https://TIL.cafe/atom.xml</id>
    <entry xml:lang="en">
        <title>Learning FreeBSD, read Absolute FreeBSD book</title>
        <published>2026-01-01T00:00:00+00:00</published>
        <updated>2026-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2026/learning-freebsd/"/>
        <id>https://TIL.cafe/blog/2026/learning-freebsd/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2026/learning-freebsd/">&lt;p&gt;I decided to start learning FreeBSD... even though I&#x27;m already running MacOS, Linux &amp;amp; Windows at home.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-learn-freebsd&quot;&gt;why learn FreeBSD?&lt;&#x2F;h2&gt;
&lt;p&gt;A whole mix of reasons.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I want more deterministic computing in my life. More stability, less enshittification of services.&lt;&#x2F;li&gt;
&lt;li&gt;Keep looking wistfully at retro computing and solarpunk and offline first computing... want things more like that.&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m personally finding the Linux world a bit overwhelming these days. So many different ways to install software, seems like distributions are increasingly diverging from each other. Also, it seems like design priorities seem to favor interactive&#x2F;desktop usage over headless&#x2F;server usage. This is great for desktop usage! But it sure seems to lead to a lot of different ways to install software on a machine.&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m want to put my limited learning time into skills I think will last. And I think FreeBSD related skills will be relevant for a long time.&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;ve heard good things about ZFS and jails and want some personal experience.&lt;&#x2F;li&gt;
&lt;li&gt;A friend of mine has been using FreeBSD for years and has offered to help when I get stuck. :)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So... giving it a go. Should help build broader and deeper experience if nothing else.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-how&quot;&gt;what &amp;amp; how&lt;&#x2F;h2&gt;
&lt;p&gt;My initial goals: use at home for self hosting needs - running web apps, file servers, hosting storage&#x2F;backups, etc.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been enjoying technical books a lot more over the last few years, so I want to start with a good overview from a book so I don&#x27;t miss fundamental stuff. Based on several recommendations, decided to read &lt;a href=&quot;https:&#x2F;&#x2F;nostarch.com&#x2F;absfreebsd3&quot;&gt;Absolute FreeBSD 3rd edition&lt;&#x2F;a&gt;. The &lt;a href=&quot;https:&#x2F;&#x2F;mwl.io&#x2F;faq#buying&quot;&gt;author recommends&lt;&#x2F;a&gt; buying this book from no starch press directly (and has a coupon code in that FAQ).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-s-that-going-so-far&quot;&gt;how&#x27;s that going so far?&lt;&#x2F;h2&gt;
&lt;p&gt;Great!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve just finished reading Absolute FreeBSD and have installed FreeBSD on two machines and fought with DNS (turns out modern caching DNS resolvers fail if the machine clock is too far off... because TLS (I should write a TIL about that)).&lt;&#x2F;p&gt;
&lt;p&gt;Overall it feels... both nostalgic and futuristic. Things just feel familiar already. I guess that&#x27;s because so many of the abstractions I&#x27;m familiar with from other OSs are &lt;em&gt;from&lt;&#x2F;em&gt; BSD and Unix in general. So... kind of retro... but also very much &lt;em&gt;now&lt;&#x2F;em&gt;. It supports USB, NVMe, EFI, lots of modern hardware, etc. It somehow tickles my retro-computing itch while being useful for everyday work.&lt;&#x2F;p&gt;
&lt;p&gt;A few things I really like so far:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Consistency. Once you learn a pattern it appears over and over.&lt;&#x2F;li&gt;
&lt;li&gt;Separation of default config from local config, making it easier and safer to upgrade. And sample files (&lt;code&gt;something.conf&lt;&#x2F;code&gt; is often paired with a &lt;code&gt;something.conf.sample&lt;&#x2F;code&gt; containing a default or example configuration).&lt;&#x2F;li&gt;
&lt;li&gt;Docs! Love the culture of documentation. Man pages actually seem to exist for everything... and they&#x27;re useful! I&#x27;m having to retrain my myself to look at local docs before jumping to the web.&lt;&#x2F;li&gt;
&lt;li&gt;Self contained. The system is a complete unit. Can learn from the docs... which are ...on the computer! Feels &lt;em&gt;wild&lt;&#x2F;em&gt; these days... so used to everything being online... and maybe not even on the web anymore, maybe in a transient chatroom.&lt;&#x2F;li&gt;
&lt;li&gt;Jails and VMs built-in, ZFS feels like virtualized storage (in a good way!) on every machine.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;about-absolute-freebsd-the-book&quot;&gt;about Absolute FreeBSD, the book&lt;&#x2F;h2&gt;
&lt;p&gt;Overall &lt;a href=&quot;https:&#x2F;&#x2F;nostarch.com&#x2F;absfreebsd3&quot;&gt;Absolute FreeBSD&lt;&#x2F;a&gt; is a gem and I highly recommend it.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Written by a real human with real experience. It shows and is much appreciated. The author often highlights a few specific features or config options which are either widely useful or easy to miss.&lt;&#x2F;li&gt;
&lt;li&gt;That said... the author definitely has a bit of a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Bastard_Operator_From_Hell&quot;&gt;BOFH&lt;&#x2F;a&gt; vibe... which will likely be fun for some folks and rub others a bit the wrong way. It&#x27;s &lt;em&gt;mostly&lt;&#x2F;em&gt; kept to footnotes, so you can skip it.&lt;&#x2F;li&gt;
&lt;li&gt;To the above note... the writing has a specific person&#x27;s voice, which I appreciate these days!&lt;&#x2F;li&gt;
&lt;li&gt;It hits a great balance between overview and details. I feel like I got a solid tour of the space, shown a few key things that would be easy to miss and have a solid base to dig into the project docs.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Absolute FreeBSD was published six years ago... in 2019. Six years is a long time in tech... but in this case it&#x27;s still &lt;em&gt;very&lt;&#x2F;em&gt; relevant and useful. The fundamentals of FreeBSD don&#x27;t appear to have changed much in that time. In my mind this is a very good thing. Successfully learning fundamentals from a six year old book supports the idea that I&#x27;ll be able to use this knowledge for many years to come. Specifically, the book talks about FreeBSD features through version 12, and version 15 was released a month ago. I did a skim through release announcements and it looks like the main updates since 12 are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Many version updates of dependencies and subsystems.&lt;&#x2F;li&gt;
&lt;li&gt;All supported architectures built with LLVM&#x2F;clang instead of gcc.&lt;&#x2F;li&gt;
&lt;li&gt;Code moved from SVN to Git.&lt;&#x2F;li&gt;
&lt;li&gt;arm64 now Tier-1 platform.&lt;&#x2F;li&gt;
&lt;li&gt;nfs servers can run in vnet jail (and pnfs support).&lt;&#x2F;li&gt;
&lt;li&gt;Renamed &lt;code&gt;blacklistd&lt;&#x2F;code&gt; --&amp;gt; &lt;code&gt;blocklistd&lt;&#x2F;code&gt; (yea! Reading &quot;blacklist&quot; in 2025 felt really jarring).&lt;&#x2F;li&gt;
&lt;li&gt;New method for updating the base system (&lt;code&gt;freebsd_update&lt;&#x2F;code&gt; --&amp;gt; &lt;code&gt;pkg&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So... the only part of the book that&#x27;s out of date is the section on upgrading the base system. Nice!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next&quot;&gt;next&lt;&#x2F;h2&gt;
&lt;p&gt;Next I&#x27;m planning to iterate a bit on exsiting services I have at home (file server, backups, network config, etc) and start adding a few more self hosted things (code repos, photos, music) and try to learn more about ZFS and jails along the way.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Book: 50 Years of Text Games</title>
        <published>2025-10-23T00:00:00+00:00</published>
        <updated>2025-10-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2025/book-50-years-of-text-games/"/>
        <id>https://TIL.cafe/blog/2025/book-50-years-of-text-games/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2025/book-50-years-of-text-games/">&lt;p&gt;I finished reading &lt;a href=&quot;https:&#x2F;&#x2F;if50.textories.com&quot;&gt;50 Years of Text Games&lt;&#x2F;a&gt; recently. It&#x27;s amazing. If you have any interest in text, stories, or games, I highly recommend it.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been slowly working through it a few chapters at a time over many months. Savoring it. The structure is inspired: deep dive on one game per year, 50 games, 50 years. Each chapter is an essay, both about the specific game and the context and times surrounding it. It works really well. I now have a &lt;em&gt;much&lt;&#x2F;em&gt; better understanding of the history of text games as a whole and mental model of many specific games. Also ended up with a list of them that I want to try at some point. Whee!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been alive and using computers for most of that time period... and it&#x27;s amazing to follow along chapter by chapter... how it sometimes connects to existing experiences (I ran a BBS with one of the door games covered in the book) or fills in blanks that I missed along the way. Some chapters were deeply nostalgic, while others covered games and &lt;em&gt;kinds of games&lt;&#x2F;em&gt; completely new to me. Really enjoyed that contrast.&lt;&#x2F;p&gt;
&lt;p&gt;Would love to read similar books on other topics! Does this remind you of anything? If so, please let me know!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Hosting a static web site using Google Cloud Run</title>
        <published>2024-04-30T00:00:00+00:00</published>
        <updated>2025-09-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2024/hosting-static-web-site-using-google-cloud-run/"/>
        <id>https://TIL.cafe/blog/2024/hosting-static-web-site-using-google-cloud-run/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2024/hosting-static-web-site-using-google-cloud-run/">&lt;h1 id=&quot;context&quot;&gt;context&lt;&#x2F;h1&gt;
&lt;p&gt;This website (&lt;a href=&quot;https:&#x2F;&#x2F;til.cafe&#x2F;&quot;&gt;https:&#x2F;&#x2F;til.cafe&#x2F;&lt;&#x2F;a&gt;) is currently a static site, hosted on Google Cloud Run. Wait... isn&#x27;t that a serverless container hosting service? Yes.  You may be asking &lt;em&gt;why&lt;&#x2F;em&gt; I&#x27;m hosting a static site on something designed for running containers? The details are in &lt;a href=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2024&#x2F;deciding-how-to-host-a-static-site-on-google-cloud&#x2F;&quot;&gt;this post about the tradeoffs&lt;&#x2F;a&gt;. The short version is that it works really well (no fiddling), and can be set up to automatically build. So I can edit text, check it in, push and the site gets updated. Nice.&lt;&#x2F;p&gt;
&lt;p&gt;So... how fiddly is it to set up? Not bad. And that&#x27;s what the rest of this article is about.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;overview&quot;&gt;overview&lt;&#x2F;h1&gt;
&lt;p&gt;OK, so Cloud Run is a service that hosts containers that serve web content. You can host pretty much any application written in any you&#x27;d like, as long as you can wrap it in a container and it responds to HTTP requests.&lt;&#x2F;p&gt;
&lt;p&gt;A static website is made from a bunch of files (at least HTML&#x2F;CSS, probably more) on disk somewhere that can be accessed via HTTP.&lt;&#x2F;p&gt;
&lt;p&gt;So... how do we connect these two? Files can&#x27;t serve themselves, so we need a web server, and then wrap it and the files into a container and send that to Cloud run. We only need two short config files to do this... but it was a bit fiddly to get there, so let&#x27;s walk through the details.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;static-website-files&quot;&gt;static website files&lt;&#x2F;h2&gt;
&lt;p&gt;I kinda skimmed it above... but while you could write the HTML&#x2F;CSS directly (and there are pretty good arguments for doing it), this site uses a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Static_site_generator&quot;&gt;Static Site Generator&lt;&#x2F;a&gt; - software which takes text files of some kind and produces the HTML files.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m currently using &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;, but for this article, it doesn&#x27;t matter, anything which will produce the files works.&lt;&#x2F;p&gt;
&lt;p&gt;In my case they&#x27;re generated by running this command:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#002b36;color:#839496;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b58900;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; zola build
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, where you see that below, replace it with whatever you use to generate the files.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;web-server&quot;&gt;web server&lt;&#x2F;h2&gt;
&lt;p&gt;Next, we need to actually serve those files over HTTP. Nginx &amp;amp; Apache are currently the most popular web servers... but for this I wanted something as easy as possible to configure with minimal dependencies (to keep the container size small). At some point, I ran across &lt;a href=&quot;https:&#x2F;&#x2F;caddyserver.com&quot;&gt;Caddy&lt;&#x2F;a&gt;, and while their marketing page is kinda over-the-top, it ships as a single file binary and the config is very clean:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#002b36;color:#839496;&quot;&gt;&lt;code&gt;&lt;span&gt;:8080 {
&lt;&#x2F;span&gt;&lt;span&gt;  root * &#x2F;srv
&lt;&#x2F;span&gt;&lt;span&gt;  file_server
&lt;&#x2F;span&gt;&lt;span&gt;  log {
&lt;&#x2F;span&gt;&lt;span&gt;    output stdout
&lt;&#x2F;span&gt;&lt;span&gt;    format json
&lt;&#x2F;span&gt;&lt;span&gt;  }  
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This says: Listen on port 8080, using the file_server module to serve all files in &lt;code&gt;&#x2F;srv&lt;&#x2F;code&gt; and log to &lt;code&gt;stdout&lt;&#x2F;code&gt; in JSON.&lt;&#x2F;p&gt;
&lt;p&gt;Some notes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;8080&lt;&#x2F;code&gt;: Cloud Run expects a process listening on 0.0.0.0 (all IPs) and port 8080 by default (see: &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;run&#x2F;docs&#x2F;container-contract&quot;&gt;Container runtime contract&lt;&#x2F;a&gt;). So I just hard-coded it here. If you need the port to be variable, check out &lt;a href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;docs&#x2F;conventions#placeholders&quot;&gt;Caddy placeholders&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;format json&lt;&#x2F;code&gt;: This is neat because Cloud Run automatically forwards stdout to Cloud Logging and it parses JSON formatted logs into structured logs you can query and filter.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;dockerfile&quot;&gt;Dockerfile&lt;&#x2F;h2&gt;
&lt;p&gt;The next step is to build a container.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m using a &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;build&#x2F;building&#x2F;multi-stage&#x2F;&quot;&gt;two-stage Docker build&lt;&#x2F;a&gt;, a neat pattern I learned from &lt;a href=&quot;https:&#x2F;&#x2F;daniel-azuma.com&#x2F;blog&#x2F;2019&#x2F;07&#x2F;01&#x2F;deploying-my-blog-to-google-cloud-run&quot;&gt;Daniel Azuma&#x27;s post &quot;Deploying My Blog to Google Cloud Run&quot;&lt;&#x2F;a&gt; First we build the static website (using Zola, in my case) and since we only want the website in the final image, we start a new image (using a pre-made Caddy image) and copy the config and web site files to it.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;dockerfile&quot; style=&quot;background-color:#002b36;color:#839496;&quot; class=&quot;language-dockerfile &quot;&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span style=&quot;color:#859900;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; alpine &lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;AS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#268bd2;&quot;&gt;build
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;RUN &lt;&#x2F;span&gt;&lt;span&gt;apk add zola
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;WORKDIR &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;workspace
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; . &#x2F;workspace
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;RUN &lt;&#x2F;span&gt;&lt;span&gt;zola build
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#586e75;&quot;&gt;####
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;FROM&lt;&#x2F;span&gt;&lt;span&gt; caddy:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b58900;&quot;&gt;2.7-alpine
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; --from=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#268bd2;&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;workspace&#x2F;Caddyfile &#x2F;etc&#x2F;caddy&#x2F;Caddyfile
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#859900;&quot;&gt;COPY&lt;&#x2F;span&gt;&lt;span&gt; --from=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#268bd2;&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;workspace&#x2F;public &#x2F;srv
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;build-and-deploy-an-image-to-cloud-run&quot;&gt;build and deploy an image to Cloud Run&lt;&#x2F;h2&gt;
&lt;p&gt;And... now we need to actually get this online. How do we get this on Cloud Run? Luckily Cloud Run can handle things from here.&lt;&#x2F;p&gt;
&lt;p&gt;Well... I have to admit that I&#x27;m assuming that the website is stored in source control. I hope yours is? Please say yes.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;briandorsey&#x2F;til.cafe&quot;&gt;source for this website (til.cafe) is here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Cloud Run can be configured to build an image and deploy that whenever a git repository changes. It supports repositories hosted by a few different services, &lt;del&gt;GitHub&lt;&#x2F;del&gt; Codeberg (see &lt;a href=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2024&#x2F;hosting-static-web-site-using-google-cloud-run&#x2F;#codeberg&quot;&gt;update&lt;&#x2F;a&gt; below) in this case.&lt;&#x2F;p&gt;
&lt;p&gt;In case things change, I&#x27;m going to refer to the &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;run&#x2F;docs&#x2F;continuous-deployment-with-cloud-build#setup-cd&quot;&gt;official docs for setting up continuous deployment&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I followed those steps, giving a service account permission to connect to GitHub, choosing the trigger (push to the main branch, in my case), and choosing &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; as the build type. Since it only needs to be done once on a personal project, I did it via the web UI, but you can &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;build&#x2F;docs&#x2F;deploying-builds&#x2F;deploy-cloud-run&quot;&gt;set things up manually&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Update from the future: I&#x27;m now deploying using &lt;code&gt;gcloud run deploy&lt;&#x2F;code&gt; from my local machine. Details in &lt;a href=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2024&#x2F;hosting-static-web-site-using-google-cloud-run&#x2F;#codeberg&quot;&gt;update&lt;&#x2F;a&gt; below.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;usage&quot;&gt;usage&lt;&#x2F;h1&gt;
&lt;p&gt;All of the above is one-time set up. In everyday usage, I edit the files for my site, test the changes locally, check them into main, and push them to GitHub. A few minutes later, the new version is online! This works from whatever tools I use to update the git repo... so it works from desktop, laptop, mobile, whatever. I&#x27;m &lt;em&gt;really&lt;&#x2F;em&gt; happy with this pattern. I haven&#x27;t had to touch it once since setting it up over a year ago.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;note-about-branches&quot;&gt;note about branches&lt;&#x2F;h2&gt;
&lt;p&gt;Reading this carefully has probably made some of you nervous... Isn&#x27;t this deploying to production on every change? Yes, pretty much, at least everything that lands on main will get deployed.&lt;&#x2F;p&gt;
&lt;p&gt;This is a small personal site, so I have it configured to build and deploy on every push to main. That probably wouldn&#x27;t make sense for anything more critical than a personal blog or anything with multiple developers. You can also trigger builds on pushes to specific branches or tags as well as pull requests. And you can setup different Cloud Run services at subdomains for each, so it&#x27;s straightforward to create an integration environment separate from production, for example. Not every push needs to go to prod. ;)&lt;&#x2F;p&gt;
&lt;h1 id=&quot;enjoy&quot;&gt;enjoy&lt;&#x2F;h1&gt;
&lt;p&gt;Happy static site hosting!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;codeberg&quot;&gt;2025-09 update - moved source to Codeberg&lt;&#x2F;h1&gt;
&lt;p&gt;As of September of 2025, I&#x27;ve moved most of my code hosting to &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;briandorsey&quot;&gt;Codeberg.org&lt;&#x2F;a&gt;. Cloud Run doesn&#x27;t currently support deployment hooks from Codeberg and I&#x27;ve started wanting to push commits more often than I update the site... so... I&#x27;m now doing deployments manually from my local machine. This is the key command:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#002b36;color:#839496;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#b58900;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span&gt; gcloud run deploy til-cafe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#268bd2;&quot;&gt; --allow-unauthenticated --max-instances&lt;&#x2F;span&gt;&lt;span style=&quot;color:#657b83;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#dc322f;&quot;&gt;\
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#268bd2;&quot;&gt;--region&lt;&#x2F;span&gt;&lt;span style=&quot;color:#657b83;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;us-west1&lt;&#x2F;span&gt;&lt;span style=&quot;color:#268bd2;&quot;&gt; --project&lt;&#x2F;span&gt;&lt;span style=&quot;color:#657b83;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;til-cafe&lt;&#x2F;span&gt;&lt;span style=&quot;color:#268bd2;&quot;&gt; --source&lt;&#x2F;span&gt;&lt;span&gt; .
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(note that last period, it&#x27;s telling &lt;code&gt;--source&lt;&#x2F;code&gt; to use source code from the local directory)&lt;&#x2F;p&gt;
&lt;p&gt;The full command I&#x27;m using also has a call to &lt;a href=&quot;https:&#x2F;&#x2F;jj-vcs.github.io&#x2F;jj&#x2F;latest&#x2F;&quot;&gt;Jujutsu&lt;&#x2F;a&gt; to append the latest commit hash to the Cloud Run revision for reproducibility. You can see the full command in the &lt;code&gt;justfile&lt;&#x2F;code&gt; in the &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;briandorsey&#x2F;til.cafe&#x2F;src&#x2F;branch&#x2F;main&#x2F;justfile&quot;&gt;til.cafe&lt;&#x2F;a&gt; source repository. Also, there is a slight variation to push to a separate staging site.&lt;&#x2F;p&gt;
&lt;p&gt;I also ended up creating a &lt;code&gt;.gcloudignore&lt;&#x2F;code&gt; file (patterned after &lt;code&gt;.gitignore&lt;&#x2F;code&gt; syntax) which &lt;code&gt;gcloud run deploy&lt;&#x2F;code&gt; uses to exclude some files from upload.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>&#x27;fq&#x27; is &#x27;jq&#x27; for binary formats</title>
        <published>2024-03-15T00:00:00+00:00</published>
        <updated>2024-03-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2024/fq-is-jq-for-binary-formats/"/>
        <id>https://TIL.cafe/blog/2024/fq-is-jq-for-binary-formats/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2024/fq-is-jq-for-binary-formats/">&lt;p&gt;Just started playing with &lt;code&gt;fq&lt;&#x2F;code&gt; a &quot;jq for binary files&quot;: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wader&#x2F;fq&#x2F;&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;wader&#x2F;fq&#x2F;&lt;&#x2F;a&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;jqlang.github.io&#x2F;jq&#x2F;&quot;&gt;&lt;code&gt;jq&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is a command line tool for parsing and manipulating JSON documents)&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s a command line tool for exploring, visualizing and parsing binary files.&lt;&#x2F;p&gt;
&lt;p&gt;Initial impression is &lt;em&gt;really&lt;&#x2F;em&gt; positive. Look at this lovely breakdown of the details of a WAVE file. Like most tools it doesn&#x27;t parse most metadata chunks, but it gives a really clear view of the structure of the chunks in a file, and at least which chunks are in a file.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img
  srcset=&quot;fq-wave.png 2x&quot;
  src=&quot;fq-wave.png&quot;
  alt=&quot;Screenshot of command line output from the fq tool parsing an example WAV file. It shows file offset, hex view of the data, ascii view of the data and a parsed interpretation of the data.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Google Cloud static site hosting options</title>
        <published>2024-02-26T00:00:00+00:00</published>
        <updated>2024-02-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2024/deciding-how-to-host-a-static-site-on-google-cloud/"/>
        <id>https://TIL.cafe/blog/2024/deciding-how-to-host-a-static-site-on-google-cloud/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2024/deciding-how-to-host-a-static-site-on-google-cloud/">&lt;p&gt;Like a lot of folks recently, I finally decided to setup a personal website&#x2F;blog. You&#x27;re reading it now! :) This post covers different options for hosting a static site on Google Cloud and what I decided in the end. Teaser: I picked something that isn&#x27;t usually considered for static sites and I&#x27;m really happy with how it worked out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-bother-hosting-your-own-blog&quot;&gt;Why bother hosting your own blog?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ll quote Scott Hanselman here:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I would encourage you &lt;em&gt;all&lt;&#x2F;em&gt; to blog more. Tweet less.&lt;&#x2F;strong&gt; Blogs are owned by you. They are easily found, easily linked to, and great conversations happen with great blog posts. The river of social media rushes on and those conversations are long forgotten. &lt;strong&gt;A great blog post is forever.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.hanselman.com&#x2F;blog&#x2F;your-blog-is-the-engine-of-community&quot;&gt;&quot;Your Blog is the Engine of Community&quot;&lt;&#x2F;a&gt; -- Scott Hanselman&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Also, I&#x27;m inspired by folks like &lt;a href=&quot;https:&#x2F;&#x2F;jvns.ca&#x2F;&quot;&gt;Julia Evans&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;&quot;&gt;Simon Willison&lt;&#x2F;a&gt; who learn in public and share what they&#x27;ve learned as they go.&lt;&#x2F;p&gt;
&lt;p&gt;I want to do more learning in public and ... because I know myself... I need make that as easy as possible or I will get distracted troubleshooting shiny tech stuff.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;goals-constraints&quot;&gt;Goals &#x2F; Constraints&lt;&#x2F;h2&gt;
&lt;p&gt;Context always matters. Here are some goals and constraints I had in mind while making this decision, so you can see how they map to yours.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;hosted on Google Cloud&lt;&#x2F;strong&gt; &lt;br &#x2F;&gt;
It&#x27;s the environment I know best. (I work there).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;text file based authoring, stored in a version control system&lt;&#x2F;strong&gt; &lt;br &#x2F;&gt;
I&#x27;m a software developer and want to use tools I&#x27;m familiar with.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;low administration overhead, hopefully zero maintenance&lt;&#x2F;strong&gt; &lt;br &#x2F;&gt;
I&#x27;ve done a lot of infrastructure work so it&#x27;s really easy to spend time fiddling with stuff instead of writing words. Writing words is hard. The more reliable things are, the less time I&#x27;ll spend fiddling with software instead of writing words.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;easy site updates&lt;&#x2F;strong&gt; &lt;br &#x2F;&gt;
It should be as easy as possible to update the site once I&#x27;m OK with the writing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;static site (mostly)&lt;&#x2F;strong&gt; &lt;br &#x2F;&gt;
Static sites are just a set of files, which should help simplify all of the above goals. But... I will likely want to add a few interactive bits someday... so &quot;mostly&quot;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;solutions-considered-tradeoffs&quot;&gt;Solutions Considered &amp;amp; Tradeoffs&lt;&#x2F;h2&gt;
&lt;p&gt;Here are the options I considered and their tradeoffs (for this goal).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;google-cloud-storage&quot;&gt;Google Cloud Storage&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;storage&#x2F;docs&#x2F;introduction&quot;&gt;Cloud Storage&lt;&#x2F;a&gt; is my favorite Google Cloud Service. There, I said it. Sorry, other services. You give it a file and a name, it keeps it for you. When you give it the name later, it gives you the file again. That&#x27;s the contract. It&#x27;s &lt;em&gt;very&lt;&#x2F;em&gt; reliable. &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;storage&#x2F;docs&#x2F;availability-durability&quot;&gt;Cloud Storage is designed for 99.999999999% (11 9&#x27;s) annual durability&lt;&#x2F;a&gt;. It&#x27;s fast and affordable.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s also... a bit fiddly when used for direct web hosting. It can do it, and there are solutions for most things you probably want to do... &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;storage&#x2F;docs&#x2F;static-website&quot;&gt;but it&#x27;s fiddly&lt;&#x2F;a&gt;. I was also a bit worried about things getting out of sync as I add and remove files over time. &lt;code&gt;gsutil rsync&lt;&#x2F;code&gt; would solve that... but I&#x27;m still diffing a collection of files for each deployment, I&#x27;d love atomic deployments and rollbacks. The big bummer for my use-case is that you can&#x27;t directly serve HTTPS traffic via a custom domain without paying a time-based (rather than usage based) fee for a load balancer. Drat.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&#x27;d like to try it out, this is a nice tutorial &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;storage&#x2F;docs&#x2F;hosting-static-website&quot;&gt;exploring static sites on Cloud Storage&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;firebase-hosting&quot;&gt;Firebase Hosting&lt;&#x2F;h3&gt;
&lt;p&gt;Well, why not &lt;a href=&quot;https:&#x2F;&#x2F;firebase.google.com&#x2F;docs&#x2F;hosting&quot;&gt;Firebase Hosting&lt;&#x2F;a&gt; then? It&#x27;s pretty much Cloud Storage underneath with a UX focused on hosting websites. Plus a CDN (content delivery network... local cached copies of the site for people around the world), neat! It handles HTTPS for custom domains, and has lots of options for previewing sites, integrating Cloud Functions, Cloud Run, GitHub integration, it can &quot;...rewrite URLs for client-side routing, set up custom headers, and even serve localized content.&quot; and, more. It&#x27;s awesome.&lt;&#x2F;p&gt;
&lt;p&gt;As someone new to it, though... it also seemed like... maybe more than I need for a small site. And even though their developer tooling UX is &lt;em&gt;excellent&lt;&#x2F;em&gt;... there are still a lot of features to think about.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Try it out: &lt;a href=&quot;https:&#x2F;&#x2F;firebase.google.com&#x2F;docs&#x2F;hosting&#x2F;quickstart&quot;&gt;Getting started with Firebase&lt;&#x2F;a&gt; tutorial.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;google-cloud-run&quot;&gt;Google Cloud Run&lt;&#x2F;h3&gt;
&lt;p&gt;&quot;&lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;run&#x2F;docs&#x2F;overview&#x2F;what-is-cloud-run&quot;&gt;Google Cloud Run&lt;&#x2F;a&gt; is a managed compute platform that lets you run containers...&quot; ... so... why am I looking at a container hosting platform for my static site? Well... partly because I&#x27;m a programmer and I&#x27;m just sure I&#x27;ll want some kind of dynamic content at some point. Yes... even when trying to keep things simple, I can&#x27;t help myself.&lt;&#x2F;p&gt;
&lt;p&gt;But keeping things to static site hosting... It handles HTTPS certificate generation and termination. Scaling up and (especially important for this blog) down to zero. Containers are designed for encapsulation, so I get atomic deployments and rollbacks. You can point Cloud Run at a GitHub repository and it will automatically build and deploy a new version of the site after every push to a branch. Neat. This seemed like a complexity sweetspot for me.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Several &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;run&#x2F;docs&#x2F;quickstarts&quot;&gt;Cloud Run quickstart tutorials&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;decision&quot;&gt;Decision&lt;&#x2F;h2&gt;
&lt;p&gt;After a bit of research and fiddling, I ended up with a nice pattern for deploying a static site to Cloud Run. The core idea is wrapping the static site data up together with a single file webserver into a docker image. With a couple of small config files checked into the repository, it will build the site HTML&#x2F;JS&#x2F;CSS, wrap it and the webserver into a container image and deploy it. On every push to the repository.&lt;&#x2F;p&gt;
&lt;p&gt;Now that it&#x27;s all set up, I edit text files and preview locally, then push when I&#x27;m ready to publish. Everything after that happens automatically. I&#x27;ve been running it this way for over a year, and haven&#x27;t had to think about it or troubleshoot anything. I consider that a win! (for this kind of site! :) )&lt;&#x2F;p&gt;
&lt;p&gt;In a future post, I&#x27;ll walk through the details of setting it up. (Update: &lt;a href=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2024&#x2F;hosting-static-web-site-using-google-cloud-run&#x2F;&quot;&gt;details post is here&lt;&#x2F;a&gt;). In the mean time, you can check out the &lt;a href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;briandorsey&#x2F;til.cafe&quot;&gt;source here&lt;&#x2F;a&gt;. In particular, the &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; and &lt;code&gt;Caddyfile&lt;&#x2F;code&gt; are all that&#x27;s needed to host the output of pretty much any static site generator via Cloud Run.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>12-bit rainbow palette from Kate Morley</title>
        <published>2024-01-26T00:00:00+00:00</published>
        <updated>2024-01-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2024/12-bit-color-palette/"/>
        <id>https://TIL.cafe/blog/2024/12-bit-color-palette/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2024/12-bit-color-palette/">&lt;p&gt;Kate Morley has published a &lt;a href=&quot;https:&#x2F;&#x2F;iamkate.com&#x2F;data&#x2F;12-bit-rainbow&#x2F;&quot;&gt;12-bit color palette&lt;&#x2F;a&gt;, made of &quot;...twelve colours chosen with consideration for how we perceive luminance, chroma, and hue.&quot; I really like it, and I wanted to document it here so I don&#x27;t lose track of it.&lt;&#x2F;p&gt;
&lt;p&gt;Example from her site:&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;svg viewBox=&quot;0 0 480 40&quot; width=&quot;480&quot; height=&quot;40&quot;&gt;
    &lt;rect fill=&quot;#817&quot; x=&quot;0&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#a36&quot; x=&quot;40&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#c66&quot; x=&quot;80&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#e94&quot; x=&quot;120&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#ed0&quot; x=&quot;160&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#9d5&quot; x=&quot;200&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#4d8&quot; x=&quot;240&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#2cb&quot; x=&quot;280&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#0bc&quot; x=&quot;320&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#09c&quot; x=&quot;360&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#36b&quot; x=&quot;400&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
    &lt;rect fill=&quot;#639&quot; x=&quot;440&quot; y=&quot;0&quot; width=&quot;40&quot; height=&quot;40&quot;&#x2F;&gt;
  &lt;&#x2F;svg&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Check out the details and design notes: &lt;a href=&quot;https:&#x2F;&#x2F;iamkate.com&#x2F;data&#x2F;12-bit-rainbow&#x2F;&quot;&gt;https:&#x2F;&#x2F;iamkate.com&#x2F;data&#x2F;12-bit-rainbow&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Convert GIFs to black and white using gifsicle</title>
        <published>2023-07-29T00:00:00+00:00</published>
        <updated>2023-07-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2023/convert-gifs-to-black-and-white-using-gifsicle/"/>
        <id>https://TIL.cafe/blog/2023/convert-gifs-to-black-and-white-using-gifsicle/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2023/convert-gifs-to-black-and-white-using-gifsicle/">&lt;p&gt;If you need to convert a GIF image from color or grey scale into just black and white, here is a recipe using a command line tool called &lt;a href=&quot;https:&#x2F;&#x2F;www.lcdf.org&#x2F;gifsicle&#x2F;&quot;&gt;gifsicle&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here is my original image (captured from the &lt;a href=&quot;https:&#x2F;&#x2F;play.date&#x2F;dev&quot;&gt;Playdate Simulator&lt;&#x2F;a&gt; included in the Playdate SDK).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2023&#x2F;convert-gifs-to-black-and-white-using-gifsicle&#x2F;rain.gif&quot; alt=&quot;alt text&quot; title=&quot;A two color, low contrast animated GIF file, showing trees with a rain animation.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And after running this command:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#002b36;color:#839496;&quot;&gt;&lt;code&gt;&lt;span&gt;gifsicle --gamma=1 --use-colormap=bw &amp;lt; rain.gif &amp;gt; rain_bw.gif
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We get a black and white image. I needed to use the gamma flag for this image, since it&#x27;s so low contrast, the default result was a solid black image.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2023&#x2F;convert-gifs-to-black-and-white-using-gifsicle&#x2F;rain_bw.gif&quot; alt=&quot;alt text&quot; title=&quot;A two color, black and white animated GIF file, showing trees with a rain animation.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And... that&#x27;s pretty much it. For more details on various options for &lt;code&gt;gifsicle&lt;&#x2F;code&gt;, see the &lt;a href=&quot;https:&#x2F;&#x2F;www.lcdf.org&#x2F;gifsicle&#x2F;man.html&quot;&gt;gifsicle docs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To go the other direction, we need a custom color map in the gifsicle format. Here is an example of one in the Playdate Simulator color scheme:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Contents of &lt;code&gt;pd_colors.txt&lt;&#x2F;code&gt;:&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#002b36;color:#839496;&quot;&gt;&lt;code&gt;&lt;span&gt;50 47 40
&lt;&#x2F;span&gt;&lt;span&gt;177 174 167
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then pass that into the &lt;code&gt;--color-map&lt;&#x2F;code&gt; flag:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#002b36;color:#839496;&quot;&gt;&lt;code&gt;&lt;span&gt;gifsicle --gamma=1 --use-colormap=pd_colors.txt &amp;lt;rain_bw.gif &amp;gt; rain_pd.gif
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2023&#x2F;convert-gifs-to-black-and-white-using-gifsicle&#x2F;rain_pd.gif&quot; alt=&quot;alt text&quot; title=&quot;A two color, low contrast animated GIF file, showing trees with a rain animation.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Backyard Rain Soundscape app for Playdate</title>
        <published>2023-07-23T00:00:00+00:00</published>
        <updated>2023-07-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2023/backyard-rain-soundscape-app-for-playdate/"/>
        <id>https://TIL.cafe/blog/2023/backyard-rain-soundscape-app-for-playdate/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2023/backyard-rain-soundscape-app-for-playdate/">&lt;p&gt;I&#x27;ve published a rain soundscape listing app for &lt;a href=&quot;https:&#x2F;&#x2F;play.date&quot;&gt;Playdate&lt;&#x2F;a&gt; handhelds.&lt;&#x2F;p&gt;
&lt;p&gt;Demo:&lt;&#x2F;p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;-W3mnNg7gSA&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;More information and download:&lt;&#x2F;p&gt;
&lt;iframe frameborder=&quot;0&quot; src=&quot;https:&#x2F;&#x2F;itch.io&#x2F;embed&#x2F;2146638?linkback=true&quot; width=&quot;552&quot; height=&quot;167&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;briandorsey.itch.io&#x2F;backyard-rain-soundscape&quot;&gt;Backyard Rain Soundscape&lt;&#x2F;a&gt;&lt;&#x2F;iframe&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Playdate development is easy and fun</title>
        <published>2023-07-07T00:00:00+00:00</published>
        <updated>2023-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2023/playdate-development-is-easy-and-fun/"/>
        <id>https://TIL.cafe/blog/2023/playdate-development-is-easy-and-fun/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2023/playdate-development-is-easy-and-fun/">&lt;p&gt;I recently got a quirky little handheld gaming device, called &lt;a href=&quot;https:&#x2F;&#x2F;play.date&quot;&gt;Playdate&lt;&#x2F;a&gt;. I had a fantastic early experience developing for it. It&#x27;s gotten me more excited about programming than anything else in years. And... I don&#x27;t understand exactly why. This is my attempt to think it through.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;TIL.cafe&#x2F;blog&#x2F;2023&#x2F;playdate-development-is-easy-and-fun&#x2F;playdate_backyard_rain.jpeg&quot; alt=&quot;alt text&quot; title=&quot;A Playdate gaming handheld. So small!&quot; &#x2F;&gt;
&lt;br&gt;&lt;em&gt;A Playdate gaming handheld. So small!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This was one of those supplychain delayed orders, so I&#x27;d mostly forgotten about it by the time it arrived. And while I was planning to use it for distraction gaming, I&#x27;m somehow finding myself developing software for it! I had no plans to make any games... but somehow the developer experience is &lt;em&gt;so&lt;&#x2F;em&gt; good, that I&#x27;ve almost accidentally started, and can&#x27;t seem to stop now! This post is about the initial dev experience and how great it is.&lt;&#x2F;p&gt;
&lt;p&gt;The first evening I played with it, I &lt;a href=&quot;https:&#x2F;&#x2F;hachyderm.io&#x2F;@briandorsey&#x2F;110625886762741255&quot;&gt;posted this&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Super impressed with the #playdate SDK. I&#x27;m a programer, but I&#x27;ve never done game dev. Had an idea for a... well more of a fidget toy than a game. An hour later and it&#x27;s running on the physical device. Was able to merge two of the examples and find a few additional functions to call from the docs and ... done!?!&lt;&#x2F;p&gt;
&lt;p&gt;The Playdate devs have done &lt;em&gt;really&lt;&#x2F;em&gt; nice work. Docs are good, tooling is good, &lt;em&gt;everything worked&lt;&#x2F;em&gt;. Stellar first impression!&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-story&quot;&gt;the story&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s walk through that in more detail. I got the device during a work week, fiddled with a few games and set it aside. A few evenings later, I thought...&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;They have development tools for it, right? I wonder if I could make a logo spin?&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Not sure why I was thinking about that exactly, but... I decided to try. Opened the &lt;a href=&quot;https:&#x2F;&#x2F;play.date&#x2F;dev&#x2F;&quot;&gt;Playdate developer site&lt;&#x2F;a&gt; and it looks like they have three options: Lua and C APIs with a local simulator, and an online authoring tool called &lt;a href=&quot;https:&#x2F;&#x2F;play.date&#x2F;pulp&#x2F;&quot;&gt;Pulp&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to go with Lua, since I&#x27;ve been meaning to learn it to script other software. Downloaded the SDK, installed, and poked around at the examples. Created a folder with a copy of &lt;code&gt;PlaydateSDK&#x2F;Examples&#x2F;Game Template&lt;&#x2F;code&gt; and that seemed to run. Started reading &lt;a href=&quot;https:&#x2F;&#x2F;sdk.play.date&#x2F;inside-playdate&quot;&gt;the reference docs&lt;&#x2F;a&gt;, which are quite clear and to-the-point.&lt;&#x2F;p&gt;
&lt;p&gt;After getting that first thing running in the simulator, I spent about 45 minutes reading docs, poking at examples and searching the &lt;a href=&quot;https:&#x2F;&#x2F;devforum.play.date&quot;&gt;developer forums&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m recreating this from memory, but I think these are the main sources I used:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PlaydateSDK&#x2F;Examples&#x2F;Single File Examples&#x2F;crank.lua&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;devforum.play.date&#x2F;t&#x2F;basic-crank-rotate-sprite&#x2F;8422&quot;&gt;https:&#x2F;&#x2F;devforum.play.date&#x2F;t&#x2F;basic-crank-rotate-sprite&#x2F;8422&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Combine those into one program... and... with that, I had everything I needed.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;It&#x27;s working?!?&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Not what I expected. I&#x27;m used to long research side quests and yak-shaving. I got something working on the device in an hour? This is fun. OK, and the crank on the side just feels good.&lt;&#x2F;p&gt;
&lt;p&gt;I have to admit that I did spend the next two hours fiddling with image dithering (for the 1-bit actually-only-black-and-white screen) and fiddling with crank response timing, adding additional button handlers, etc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thoughts&quot;&gt;thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;OK, but... &lt;em&gt;why&lt;&#x2F;em&gt; was this so much easier to get started and more fun than I expected? After thinking about it and talking it over with colleages for a few days... I think... it&#x27;s not magic, the Panic folks have done all the fundamentals really well.&lt;&#x2F;p&gt;
&lt;p&gt;Things that worked well for me (first time Playdate developer):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I could choose between multiple tools which support different levels of abstraction and programming experience.&lt;&#x2F;li&gt;
&lt;li&gt;The simualator works well and allows quick iterations.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;sdk.play.date&#x2F;2.0.0&#x2F;Inside%20Playdate.html&quot;&gt;reference docs&lt;&#x2F;a&gt; are accurate, comprehensive, concise and ... crucially: all in one place. I just scrolled up and down the same page with an occasional search-in-page.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;devforum.play.date&quot;&gt;developer forums&lt;&#x2F;a&gt; are full of useful posts.&lt;&#x2F;li&gt;
&lt;li&gt;Constraints simplify. The device and APIs are very focused. It think it could be possible to... just learn them all. Most dev tools feel like they&#x27;re growing faster than I can keep up.&lt;&#x2F;li&gt;
&lt;li&gt;Seeing something you just made on a physical device in your hand just feels good.&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m of an age where 1-bit visuals are super nostalgic, so I&#x27;m sure that helps.&lt;&#x2F;li&gt;
&lt;li&gt;And maybe most of all: fast feedback loops. In Lua at least, pretty much everything I tried just worked or failed right away. Edit, save, view, repeat. Once things are working well enough, it only takes 15-20 seconds to get that build from the simulator to the physical device.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;your-turn&quot;&gt;your turn&lt;&#x2F;h2&gt;
&lt;p&gt;I want everyone to experience this! ... but... today at least, they&#x27;re backordered for many months. That said, the &lt;a href=&quot;https:&#x2F;&#x2F;play.date&#x2F;dev&#x2F;#cardSDK&quot;&gt;local dev tools&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;play.date&#x2F;pulp&#x2F;&quot;&gt;Pulp&lt;&#x2F;a&gt; (online authoring tool) are free and work without a device. Consider playing around with them.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re at all curious (and can afford to), order one and forget about it until it arrives. Your future self will thank you.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>RPN calculator tutorial</title>
        <published>2023-03-18T00:00:00+00:00</published>
        <updated>2023-03-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2023/rpn-tutorial/"/>
        <id>https://TIL.cafe/blog/2023/rpn-tutorial/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2023/rpn-tutorial/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;hansklav.home.xs4all.nl&#x2F;rpn&#x2F;&quot;&gt;RPN Tutorial, incl. some things HP did not tell&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;RPN: Reverse Polish Notation. Or postfix notation or stack logic, the calculator logic system used in many Hewlett-Packard (HP) calculators.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d heard of and slightly poked at RPN calculators a few times in the past. Not sure why, but I gave them another look recently and this tutoral made them finally &lt;em&gt;click&lt;&#x2F;em&gt; for me. It&#x27;s very good. If you&#x27;ve ever been curious about RPN or HP calculators, give it a look. It also includes links to many simulators. Also useful if you&#x27;re an RPN person trying to get someone else into them. :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>About</title>
        <published>2023-01-31T00:00:00+00:00</published>
        <updated>2023-01-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/about/"/>
        <id>https://TIL.cafe/about/</id>
        
        <content type="html" xml:base="https://TIL.cafe/about/">&lt;h1 id=&quot;til-today-i-learned&quot;&gt;TIL = Today I Learned&lt;&#x2F;h1&gt;
&lt;p&gt;Hi!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m Brian Dorsey... and this is my website.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine a relaxed cafe where folks are hanging out, working on projects and
sharing what they&#x27;ve learned recently. This site is where I&#x27;m sharing public
notes, things I&#x27;ve learned recently, project updates, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully you find some of it helpful and use it to do something important to you.&lt;&#x2F;p&gt;
&lt;p&gt;Take care,&lt;&#x2F;p&gt;
&lt;p&gt;   -Brian&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Nerd Font patcher on MacOS</title>
        <published>2023-01-14T00:00:00+00:00</published>
        <updated>2023-01-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2023/nerd-font-patcher/"/>
        <id>https://TIL.cafe/blog/2023/nerd-font-patcher/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2023/nerd-font-patcher/">&lt;p&gt;&quot;&lt;a href=&quot;https:&#x2F;&#x2F;www.nerdfonts.com&#x2F;&quot;&gt;Nerd Fonts&lt;&#x2F;a&gt; patches developer targeted fonts with a high number of glyphs (icons). Specifically to add a high number of extra glyphs from popular ‘iconic fonts’ such as Font Awesome, Devicons, Octicons, and others.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Nerd Fonts looks like a very nice way to get a lot of extra visuals at the command line, lots of programming related icons and shapes. They even have downloads of popular programming fonts patched with the extra icons. So if you use one of them, head to &lt;a href=&quot;https:&#x2F;&#x2F;www.nerdfonts.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.nerdfonts.com&#x2F;&lt;&#x2F;a&gt;, download their patched version and you&#x27;re good to go.&lt;&#x2F;p&gt;
&lt;p&gt;However, I&#x27;ve recently switched to a paid programming font (&lt;a href=&quot;https:&#x2F;&#x2F;berkeleygraphics.com&#x2F;typefaces&#x2F;berkeley-mono&#x2F;&quot;&gt;Berkely Mono&lt;&#x2F;a&gt;, it&#x27;s lovely) and as you&#x27;d expect for a paid font, it&#x27;s not included. Luckily, the Nerd Font folks have a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ryanoasis&#x2F;nerd-fonts#font-patcher&quot;&gt;patcher&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;I got a bit confused about how to run it on MacOS, but once it all came together, it worked great. Here&#x27;s what worked for me, as of 2023-01-14:&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m on MacOS, and use &lt;a href=&quot;https:&#x2F;&#x2F;brew.sh&quot;&gt;homebrew&lt;&#x2F;a&gt;, so:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#002b36;color:#839496;&quot;&gt;&lt;code&gt;&lt;span&gt;$ brew install fontforge
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then go to &lt;a href=&quot;https:&#x2F;&#x2F;www.nerdfonts.com&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.nerdfonts.com&#x2F;&lt;&#x2F;a&gt;, scroll to the bottom and download FontPatcher.zip, and unzip that somehwere temporarily.&lt;&#x2F;p&gt;
&lt;p&gt;I also copied the font I wanted to patch into that same directory.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#002b36;color:#839496;&quot;&gt;&lt;code&gt;&lt;span&gt;$ fontforge -script .&#x2F;font-patcher --complete BerkeleyMono-Regular.otf
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The script created a new (&lt;em&gt;much&lt;&#x2F;em&gt; larger) font named &lt;code&gt;Berkeley Mono Regular Nerd Font Complete.otf&lt;&#x2F;code&gt;. I copied that to &lt;code&gt;&#x2F;Users&#x2F;USER&#x2F;Library&#x2F;Fonts&#x2F;&lt;&#x2F;code&gt; (where USER is my local username) and updated my terminal program&#x27;s config to use this. Done!&lt;&#x2F;p&gt;
&lt;p&gt;Now I can experiment with some of the recent command line tools which use these icons, like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Peltoche&#x2F;lsd&quot;&gt;lsd&lt;&#x2F;a&gt; (&lt;code&gt;ls&lt;&#x2F;code&gt; replacement), &lt;a href=&quot;https:&#x2F;&#x2F;zellij.dev&quot;&gt;Zellij&lt;&#x2F;a&gt; (like tmux&#x2F;screen), and &lt;a href=&quot;https:&#x2F;&#x2F;starship.rs&quot;&gt;Starship&lt;&#x2F;a&gt; (fancy shell prompt).&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Interview with Richard Hipp: Story of SQLite</title>
        <published>2023-01-02T00:00:00+00:00</published>
        <updated>2023-01-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2023/sqlite-story/"/>
        <id>https://TIL.cafe/blog/2023/sqlite-story/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2023/sqlite-story/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;corecursive.com&#x2F;066-sqlite-with-richard-hipp&#x2F;&quot;&gt;CORECURSIVE #066
The Untold Story of SQLite
With Richard Hipp&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This interview gives a lot of context on where SQLite came from, and how it grew to be the project it is now. SQLite has a somewhat different approach to software compared to many other open source projects, (few dependencies, &lt;em&gt;lot&#x27;s&lt;&#x2F;em&gt; of testing, small core team), and it&#x27;s been quite effective.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Use iOS and Git? Check out Working Copy</title>
        <published>2022-12-27T00:00:00+00:00</published>
        <updated>2022-12-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2022/working-copy-is-handy/"/>
        <id>https://TIL.cafe/blog/2022/working-copy-is-handy/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2022/working-copy-is-handy/">&lt;p&gt;If you use git and have an iOS device, check it out: &lt;a href=&quot;https:&#x2F;&#x2F;workingcopy.app&quot;&gt;https:&#x2F;&#x2F;workingcopy.app&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It works on both phones and iPads, has an embedded editor and supports several methods of integrations with other applications. I&#x27;ve mostly used it for small updates, but it seems to have enough features to support even complex workflows.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Kroki creates diagrams from textual descriptions</title>
        <published>2022-12-26T00:00:00+00:00</published>
        <updated>2022-12-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2022/kroki-diagram-api/"/>
        <id>https://TIL.cafe/blog/2022/kroki-diagram-api/</id>
        
        <summary type="html">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;kroki.io&quot;&gt;Kroki&lt;&#x2F;a&gt; is an API and interactive web site which creates diagrams from text.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Hello World!</title>
        <published>2022-12-25T00:00:00+00:00</published>
        <updated>2022-12-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Brian Dorsey
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://TIL.cafe/blog/2022/hello/"/>
        <id>https://TIL.cafe/blog/2022/hello/</id>
        
        <content type="html" xml:base="https://TIL.cafe/blog/2022/hello/">&lt;p&gt;Hello World!&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
