<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Terin Stock</title>
    <link>https://terinstock.com/</link>
    <description>Recent content on Terin Stock</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>terin@terinstock.com (Terin Stock)</managingEditor>
    <webMaster>terin@terinstock.com (Terin Stock)</webMaster>
    <copyright>© 2022 Terin Stock</copyright>
    <lastBuildDate>Mon, 19 Jun 2023 21:30:55 +0000</lastBuildDate><atom:link href="https://terinstock.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Creating Self-Hosted Tile Maps from OpenStreetMap Data</title>
      <link>https://terinstock.com/post/2023/06/Creating-Self-Hosted-Tile-Maps-from-OpenStreetMap-Data/</link>
      <pubDate>Mon, 19 Jun 2023 21:30:55 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2023/06/Creating-Self-Hosted-Tile-Maps-from-OpenStreetMap-Data/</guid>
      <content type="html">&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/46/37cf2cce580db1a9863996e0037ab83ef0bce4ab3b1f6bf51acd3ef7a0e37d.png&#34;
         alt=&#34;The work-in-progess homepage of NLAlerts, showing the most recent alerts.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;The work-in-progess homepage of NLAlerts, showing the most recent alerts.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;ve been working on &lt;a href=&#34;https://nlalerts.terin.nl&#34;&gt;NLAlerts&lt;/a&gt;, a project to archive and display the mobile emergency alerts sent by the Netherlands government. The data provided by the government includes the text of the alert, along with targetted geographical area. With JavaScript libraries such as &lt;a href=&#34;https://leafletjs.com/&#34;&gt;Leaflet&lt;/a&gt; or &lt;a href=&#34;https://openlayers.org/&#34;&gt;OpenLayers&lt;/a&gt; it is easy to turn this geographical area onto an interactive map.&lt;/p&gt;
&lt;p&gt;These libraries can be used with external tile servers, such as the one provided by OpenStreetMap or one of the many commercial offerings. However, I wanted self-host all the data required for the website, which includes the map tiles. Rendered tiles can be stored in a SQLite database following the &lt;a href=&#34;https://github.com/mapbox/mbtiles-spec&#34;&gt;MBTiles Specification&lt;/a&gt;. Since the NLAlerts site already uses SQLite by way of being powered by data exploration framework &lt;a href=&#34;https://datasette.io&#34;&gt;Datasette&lt;/a&gt;, using MBTiles would be fairly complimentary. In fact, there&amp;rsquo;s already a plugin &lt;a href=&#34;https://datasette.io/plugins/datasette-tiles&#34;&gt;datasette-tiles&lt;/a&gt; to have Datasette act as a tileserver.&lt;/p&gt;
&lt;p&gt;This just left one remaining problems: &lt;em&gt;How do I get OpenStreetMap data into MBTiles?&lt;/em&gt; I found many guides online, but they seemed to fall into one of two camps: they were written many years ago and they suffer from linkrot or bitrot, or they ripped tiles from other servers. The former problem makes it hard to follow these guides in 2023, the latter is often against the terms of service (or rude to the remaining operators of non-profit tileservers).&lt;/p&gt;
&lt;p&gt;This past weekend I was able to cobble together a working pipeline to &amp;ldquo;pre-render&amp;rdquo; tiles from OpenStreetMap extracts to MBTiles.&lt;/p&gt;
&lt;p&gt;I first started by downloading the &amp;ldquo;osm.pbf&amp;rdquo; file for the Netherlands from &lt;a href=&#34;https://download.geofabrik.de/europe/netherlands.html&#34;&gt;Geofabrik&lt;/a&gt;. This file is the OpenStreetMap data in Protobuf format.&lt;/p&gt;
&lt;p&gt;I then used the &lt;a href=&#34;https://github.com/Overv/openstreetmap-tile-server&#34;&gt;openstreetmap-tile-server&lt;/a&gt; container to import the &amp;ldquo;osm.pbf&amp;rdquo; file into PostGIS. I created volumes to store the database (&lt;code&gt;osm-data&lt;/code&gt;) as well as where the tiles would later be rendered to (&lt;code&gt;osm-tiles&lt;/code&gt;).&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;podman volume create osm-data
podman volume create osm-tiles

podman run -v $PWD/netherlands-latest.osm.pbf \
           -v osm-data:/data/database \
           -v osm-tiles:/data/tiles \
           overv/openstreetmap-tile-server:2.3.0 \
           import
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After several minutes, the container should exit successfully and all the data will be imported. The same image could be used to run the renderer. I had to increase the shared memory configured in the container to avoid rendering errors later.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;podman run -v osm-data:/data/database \
           -v osm-tiles:/data/tiles \
           -shm-size=1G \
           overv/openstreetmap-tile-server:2.3.0 \
           run
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In another terminal we could exec into this container to start the pre-rendering.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;podman exec -it --latest bash
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Within the container, we can fetch the &lt;code&gt;render_list_geo.pl&lt;/code&gt; script, which wraps the &lt;code&gt;render_list&lt;/code&gt; command to render within a bounding box at different zoom levels. I then ran the script with the bounding box determined with the  &lt;a href=&#34;https://boundingbox.klokantech.com/&#34;&gt;Bounding Box Tool&lt;/a&gt; for zoom levels 8 through 13.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;wget https://raw.githubusercontent.com/alx77/render_list_geo.pl/master/render_list_geo.pl
perl ./render_list_geo.pl -z 8 -Z 13 \
    -x 3.3316001 -X 7.2275102 \
    -y 50.7503838 -Y 53.6316
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If all went well, we can exit and stop the container, now having completed the hardest part. We have the map rendered in the &lt;code&gt;osm-tiles&lt;/code&gt;, unfortunately not in a format we can directly use, but instead in &lt;a href=&#34;https://wiki.openstreetmap.org/wiki/Meta_tiles&#34;&gt;meta tile&lt;/a&gt;, which can be efficiently used by &lt;code&gt;mod_tile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Fortunately, Geofabrik has a tool to help us out, &lt;a href=&#34;https://github.com/geofabrik/meta2tile&#34;&gt;meta2tile&lt;/a&gt;, which can be used to generate MBTiles databases. After cloning the repository, I built the tool in the most barebones configuration, only enabling MBTiles support.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;gcc -DWITH_MBTILES meta2tile.c -o meta2tile -lsqlite3 -lcrypto -lm -O3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&amp;rsquo;s worth noting that &lt;code&gt;meta2tile&lt;/code&gt; uses the deprecated &lt;code&gt;MD5&lt;/code&gt; function from OpenSSL, which may stop being provided in a future version of OpenSSL. Fortunately, it&amp;rsquo;s also not the most difficult function to replace.&lt;/p&gt;
&lt;p&gt;On my system Podman runs in rootless mode, with volumes being plain directories on disks. Thus I was able to run &lt;code&gt;meta2tile&lt;/code&gt; directly against this directory. You may need to mount the volume or copy the data out first.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;meta2tile --mbtiles \
          --meta name=&amp;#34;Netherlands&amp;#34; \
          --meta type=raster \
          --meta format=png \
          --meta version=1.0 \
          --meta bounds=&amp;#34;3.3316001,50.7503838,7.2275102,53.6316&amp;#34; \
          --meta description=&amp;#34;OpenStreetMap tiles for Netherlands&amp;#34;
          /home/terin/.local/share/containers/storage/volumes/osm-tiles/_data/default/ \
          nl.db
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I was then able to start Datasette with the datasette-tiles plugin installed and browse to &lt;code&gt;http://localhost:8001/-/tiles/nl/&lt;/code&gt; and browse my tiles.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/91/b5d02f42066b1b588292231c3386688099374679ad8d6a3fe81731ff322f6a.png&#34;
         alt=&#34;The alert page showing a test alert sent, rendering the targetted area with OpenLayers.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;The alert page showing a test alert sent, rendering the targetted area with OpenLayers.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;When creating my template for the custom alert pages, I was able to reference &lt;code&gt;&amp;quot;/-/tiles/nl/{z}/{x}/{y}.png&amp;quot;&lt;/code&gt; as the location of the tileserver. The result can be seen on this &lt;a href=&#34;https://nlalerts.terin.nl/alerts/c2006b50-7179-498d-bddd-933899f354bd&#34;&gt;test alert&lt;/a&gt;.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Controlling the XM PCR receiver from Linux</title>
      <link>https://terinstock.com/post/2023/05/Controlling-the-XM-PCR-receiver-from-Linux/</link>
      <pubDate>Mon, 08 May 2023 20:04:12 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2023/05/Controlling-the-XM-PCR-receiver-from-Linux/</guid>
      <content type="html">&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/df/68e3d31d315e0a6cdc75d048d8ab99ba727105ce483c473781d0572758ab3e:800.jpg&#34;
         alt=&#34;The XM PCR in question, complete with mysterious screw holes.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;The XM PCR in question, complete with mysterious screw holes.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The XM PCR was a satellite radio receiver sold to consumers in 2003 that was controllable by a PC running Windows over USB. Other than satellite, audio and USB connectivity, the receiver does not have any form of user interactivity, everything is done over USB. XM Radio discontinued it in 2004 due to software to easily rip songs, which was discussed on &lt;a href=&#34;https://archive.org/details/The_Tech_Guy_Audio_71/20040904-1.mp3&#34;&gt;The Tech Guy #71&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been nearly 20 years since the device was released, and the SiriusXM satellites are still broadcasting audio in a format this receiver can understand. I was shocked when the activation signal went through and the radio sprang to life. With the radio working, audio compression artifacts and all, I sought to control it from Linux.&lt;/p&gt;
&lt;p&gt;The receiver uses an FTDI component to expose a serial port over USB. This is supported by modern Linux, and after attaching the receiver to the computer, a serial device was exposed under &lt;code&gt;/dev&lt;/code&gt;. This was off to a promising start.&lt;/p&gt;
&lt;p&gt;After a little bit of investigation I found that the protocol had already been reverse engineered and Michael Minn had released a GUI program &lt;a href=&#34;https://michaelminn.com/linux/mmxmpcr/&#34;&gt;MMXMPCR&lt;/a&gt; in 2003. It was last updated in 2005 which was a long time ago in the realm of Linux desktop software. I wasn&amp;rsquo;t even sure if modern distributions still shipped the required libraries, and if they did, if the program would even compile. Fortunately, Michael still hosts the source tarballs on their website; I downloaded the latest release to find out.&lt;/p&gt;
&lt;p&gt;For the moment, Linux distributions still ship the necessary &lt;a href=&#34;https://en.wikipedia.org/wiki/Motif_(software)&#34;&gt;MOTIF&lt;/a&gt; and &lt;a href=&#34;https://en.wikipedia.org/wiki/X_Toolkit_Intrinsics&#34;&gt;X Toolkit Intrinsics&lt;/a&gt; libraries. As desktop Linux continues the migration to Wayland, I wonder how much longer this will hold true. I modified the makefile to specify the correct header location and the linker flag for the Intrinsics library. I ran make and was greeted with a &lt;code&gt;mmxmpcr&lt;/code&gt; binary, and a wall of compiler warnings.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/4f/e9aef17a793fdae00505a470623f105f43dc9de072959e0772c9247d657abb.png&#34;
         alt=&#34;Screenshot of mmxmpcr, showing the first 10 channels in the application window.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Screenshot of mmxmpcr, showing the first 10 channels in the application window.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Upon running &lt;code&gt;mmxmpcr&lt;/code&gt;, a window appeared which began populating with channel information from the radio, and I could control the receiver without issue. To remain cool, I tuned into TikTok Radio and attempted to dance.&lt;/p&gt;
&lt;p&gt;In order to preserve the project into the future, I&amp;rsquo;ve used the available release tarballs to create the &lt;a href=&#34;https://github.com/terinjokes/mmxmpcr&#34;&gt;mmxmpcr repository&lt;/a&gt; on GitHub. It would be interesting to refactor the project to use &lt;a href=&#34;https://libusb.info/&#34;&gt;libusb&lt;/a&gt; to discover an attached radio, rather than hardcoding a path into the binary.&lt;/p&gt;
&lt;p&gt;However, the original Windows software distributed by XM Radio for this receiver seems missing from the Internet Archive. If you have it, please upload it and let me know!&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>HPSS-Disassembly Progress Report (May 2022)</title>
      <link>https://terinstock.com/post/2022/05/HPSS-Disassembly-Progress-Report-May-2022/</link>
      <pubDate>Tue, 31 May 2022 16:16:16 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2022/05/HPSS-Disassembly-Progress-Report-May-2022/</guid>
      <content type="html">&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/ce/00bb21b49f9f4f766b4c3444d6f679f230a38970fd6b136474e5679264cc4c.png&#34;
         alt=&#34;Title screen of Harry Potter and the Sorcerer&amp;amp;rsquo;s Stone for the Game Boy Color&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Title screen of &lt;em&gt;Harry Potter and the Sorcerer&amp;rsquo;s Stone&lt;/em&gt; for the Game Boy Color&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;A little over 20 years ago the first movie in the &lt;em&gt;Harry Potter&lt;/em&gt; series, &lt;em&gt;Harry
Potter and the Sorcerer&amp;rsquo;s Stone&lt;/em&gt; in the United States, was released by Warner
Bros. I had been caught up in the Harry Potter mania and received the movie tie-in
Game Boy Color game as a Christmas gift. I recall playing it for a while, before
my attention returned to the other hype machine of 2001: the &lt;em&gt;Pokémon&lt;/em&gt; series.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://www.fdossena.com/?p=hp1/i.md&#34; target=&#34;_blank&#34;&gt;&lt;img src=&#34;https://terinstock.com/media/73/0524f0dccdee9e9fca1e241c9d76f1ef7837d40ed029b4841c64a6675d9de5:800.jpg&#34;
         alt=&#34;Harry Potter, Ron Weasley, and Hermione Granger running through a Hogwarts hallway in the PC version.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;p&gt;&lt;em&gt;Harry Potter and the Sorcerer&amp;rsquo;s Stone&lt;/em&gt; on PC. Image credit Federico Dossena.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The PlayStation and PC versions of the tie-in game are 3D action-puzzle games
where you play as the titular Harry Potter as he navigates around Hogwarts, the
grounds, the Forbidden Forest, and a side quest to Diagon Alley. The PlayStation
version is where we get the low-poly meme Hagrid from.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/ff/34b0288d7ba9a34ad693fbef6d358d5e1b573546c2f9dbec7952e10902c495.png&#34;
         alt=&#34;Harry Potter fighting bats in a Final Fantasy-inspired battle system.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Harry Potter fighting bats in a Final Fantasy-inspired battle system.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The Game Boy Color version was completely different: a top-down Role Playing
Game in the style of classic &lt;em&gt;Final Fantasy&lt;/em&gt;. While you still play as Harry
Potter, it follows the plot of the book much more faithfully than the 3D
counterparts. There are some changes to adapt into a game: spells are learned
for combat, items can be equipped for buffs, and wizard cards can be collected
and used for combat effects.&lt;/p&gt;
&lt;p&gt;I had entirely forgotten about this game until YouTube channel
&lt;a href=&#34;https://www.youtube.com/channel/UC9i9MfllgUd2Z6gSEGK3Vaw&#34;&gt;Flandrew&lt;/a&gt; put out a
comparison of every version of &lt;em&gt;Harry Potter and the Philosopher&amp;rsquo;s Stone&lt;/em&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;,
jogging back a flood of forgotten memories.&lt;/p&gt;
&lt;p&gt;Picking it up again, I was excited by how colorful, expansive, and smooth the
game felt, especially compared to Pokémon titles of the time. I decided I would
learn more about how the game ticked.&lt;/p&gt;
&lt;h2 id=&#34;disassembly-beginnings&#34;&gt;Disassembly Beginnings&lt;/h2&gt;
&lt;p&gt;Disassembling Game Boy games isn&amp;rsquo;t new; &lt;em&gt;Pokémon Red and Blue&lt;/em&gt; has been
&lt;a href=&#34;https://github.com/pret/pokered&#34;&gt;completely reverse engineered&lt;/a&gt; (this has
prompted projects disassembling the rest of the Pokémon series). Other classics,
such as &lt;a href=&#34;https://github.com/zladx/LADX-Disassembly&#34;&gt;&lt;em&gt;Zelda: Link&amp;rsquo;s Awakening
DX&lt;/em&gt;&lt;/a&gt;, have also been in progress for
years.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve &lt;a href=&#34;https://github.com/terinjokes/HPSS-Disassembly&#34;&gt;started a project&lt;/a&gt; to
disassemble &lt;em&gt;Harry Potter and the Sorcerer&amp;rsquo;s Stone&lt;/em&gt; for the Game Boy Color. My
goal is to document the techniques used to develop Game Boy games during the
final years of the hardware lifecycle. I&amp;rsquo;m also planning on taking techniques
learned from this project forward towards disassembling the sequel, &lt;em&gt;Harry
Potter and the Chamber of Secrets&lt;/em&gt;, which also holds the distinction of being
the last Game Boy Color game released in North America.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;ve never worked on disassembling a game before, I had to start from
scratch. Fortunately, the Game Boy homebrew development scene is quite active,
so modern tools are available to help get started.&lt;/p&gt;
&lt;h3 id=&#34;mgbdis-the-disassembler&#34;&gt;mgbdis, the disassembler&lt;/h3&gt;
&lt;p&gt;To begin understanding what&amp;rsquo;s happening &amp;ldquo;behind the curtain&amp;rdquo; we need to turn the
binary code the Game Boy Color CPU executes back into assembly code.&lt;/p&gt;
&lt;p&gt;Unfortunately, the labels and symbols are not preserved in the binary code, so
we&amp;rsquo;ll never be able to reproduce the exact structure the original game
developers had for the game. However, we can use computers to make best guesses
as a starting point, and use logic and reason (and our own guesses) to craft a
structure back on top.&lt;/p&gt;
&lt;p&gt;I used Matt Currie&amp;rsquo;s &lt;a href=&#34;https://github.com/mattcurrie/mgbdis&#34;&gt;mgbdis&lt;/a&gt; disassembler
to begin this task. &lt;em&gt;Sorcerer&amp;rsquo;s Stone&lt;/em&gt; is a 4 MiB game, so disassembling took a
while, but it ended up creating 256 files each representing 16 KiB of bankable
memory. I also attempted to use Matt&amp;rsquo;s emulator, &lt;a href=&#34;https://mattcurrie.com/bdm/&#34;&gt;Beaten Dying
Moon&lt;/a&gt;, to automate generating a symbol file to aid
in separating executing code from data like images, but it did not seem very
successful.&lt;/p&gt;
&lt;h3 id=&#34;rgbds-the-assembler-and-linker&#34;&gt;RGBDS, the assembler and linker&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://terinstock.com/media/96/3a82c9766776a2b8fcc77552b1f34d9f8a5befae96de65d1fec70f074510f9.png&#34; alt=&#34;RGBDS logo&#34;&gt;&lt;/p&gt;
&lt;p&gt;Once we have disassembled the game (even with our imperfect results) in order to
get back to a playable game again we&amp;rsquo;ll need an assembler and linker.
Fortunately we have &lt;a href=&#34;https://rgbds.gbdev.io/&#34;&gt;RGBDS&lt;/a&gt;, an open source toolchain
dating back to the 1990s.&lt;/p&gt;
&lt;p&gt;RGBDS&amp;rsquo;s assembler, &lt;a href=&#34;https://rgbds.gbdev.io/docs/v0.5.2/rgbasm.1&#34;&gt;&lt;code&gt;rgbasm&lt;/code&gt;&lt;/a&gt;,
takes the assembly files as inputs and turned them into object files. It&amp;rsquo;s
likely down the road we&amp;rsquo;ll have object files containing code that are logically
related to each other, but for now we assemble each bank into it&amp;rsquo;s own object
file.&lt;/p&gt;
&lt;p&gt;RGBDS&amp;rsquo;s linker, &lt;a href=&#34;https://rgbds.gbdev.io/docs/v0.5.2/rgblink.1&#34;&gt;&lt;code&gt;rgblink&lt;/code&gt;&lt;/a&gt;,
collects the object files together and decides how to combine them together into
a Game Boy ROM. As the code is &amp;ldquo;fixed&amp;rdquo; in memory, the primary responsibility of
the linker in this project is to resolve symbol references across object files,
so the correct memory locations can be written into the ROM. As we cleanup the
assembly files, the linker will likely become more important.&lt;/p&gt;
&lt;p&gt;RGBDS includes a header fixer,
&lt;a href=&#34;https://rgbds.gbdev.io/docs/v0.5.2/rgbfix.1&#34;&gt;&lt;code&gt;rgbfix&lt;/code&gt;&lt;/a&gt;, used to generate a
valid Game Boy header. The original hardware uses information in this header as
checksums, to setup compatibility modes, and to implement basic DRM with the
&amp;ldquo;Nintendo&amp;rdquo; logo. When the game was disassembled this header was partially
decoded as instructions, and partially as data. It was removed and replaced by
padding so &lt;code&gt;rgbfix&lt;/code&gt; could be used instead. This allows for greater flexability
in generating debug builds later, as the tool can generate the correct checksums
later.&lt;/p&gt;
&lt;p&gt;One other included tool is RGBDS&amp;rsquo;s image converter,
&lt;a href=&#34;https://rgbds.gbdev.io/docs/v0.5.2/rgbgfx.1&#34;&gt;&lt;code&gt;rgbgfx&lt;/code&gt;&lt;/a&gt;. This is a tool for
storing graphics as PNGs instead of &lt;code&gt;2bpp&lt;/code&gt;, an encoding more suitable for the
Game Boy&amp;rsquo;s hardware. &lt;code&gt;mgbdis&lt;/code&gt; did not separate the game&amp;rsquo;s images into individual
files, so we won&amp;rsquo;t be using it for now, but it will be indespensible later once
we&amp;rsquo;ve extracted the images.&lt;/p&gt;
&lt;p&gt;The RGBDS project also documents &lt;a href=&#34;https://rgbds.gbdev.io/docs/v0.5.2/rgbds.5&#34;&gt;the object file
format&lt;/a&gt;, allowing for project
specific tools to be written (if that proves to be necessary). Since &lt;em&gt;Sorcerer&amp;rsquo;s
Stone&lt;/em&gt; is a JRPG, there is a lot of dialog, menus, and world building, resulting
in lots of text. The game also supports 11 different languages, farther
multiplying the amount of text. I suspect we might need tooling to easily handle
all of it.&lt;/p&gt;
&lt;h3 id=&#34;gup-the-recursive-build-system&#34;&gt;gup, the recursive build system&lt;/h3&gt;
&lt;p&gt;After disassembling a game, mgbdis generates a basic GNU Make-compatible
Makefile. This Makefile calls &lt;code&gt;rgbasm&lt;/code&gt; over a single assembly file &amp;ldquo;game.asm&amp;rdquo;
that simply includes all the banks. For a small game this might work, but for a
large game like &lt;em&gt;Sorcerer&amp;rsquo;s Stone&lt;/em&gt; re-assembling the entire game each rebuild
was actually taking a significant amount of time. It would be far better to only
reassemble the files that changed, and the targets that depend on that file.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve switched the project to using &lt;a href=&#34;https://github.com/timbertson/gup&#34;&gt;gup&lt;/a&gt;, a
recursive build system inspired by Daniel J. Bernstein&amp;rsquo;s
&lt;a href=&#34;https://cr.yp.to/redo.html&#34;&gt;redo&lt;/a&gt;. In gup, targets are executable scripts
written in any language, and they can discover and register their own
dependencies.&lt;/p&gt;
&lt;p&gt;In HPSS-Disassembly, gup assembles banks by calling &lt;a href=&#34;https://github.com/terinjokes/HPSS-Disassembly/blob/7cf6b905052180b0f86adf24c4c9b4a2a5aca5b4/scripts/as&#34;&gt;a
script&lt;/a&gt;
that calls &lt;code&gt;rgbasm&lt;/code&gt; and registers each file it lists as a dependency with gup.
When a file is changed, gup knows it only needs to rebuild banks that included
it the previous build. This makes the testing iteration cycle extremely fast.&lt;/p&gt;
&lt;h2 id=&#34;disassembly-progress&#34;&gt;Disassembly Progress&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve written a lot of words here about the progress made on project
infrastructure. What have I actually accomplished on the disassembly side? Since
this is my first month working on the project, I&amp;rsquo;m afraid I haven&amp;rsquo;t accomplished
too much.&lt;/p&gt;
&lt;h3 id=&#34;ready-lets-start&#34;&gt;Ready? Let&amp;rsquo;s Start&lt;/h3&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/54/634c2d974d0a1fa20ac746942cee8e85844405b6032ed7cfb40456370043b9.png&#34;
         alt=&#34;Error displayed when the game is inserted into a system not compatible with the Game Boy Color.&#34;/&gt;&lt;figcaption&gt;
            &lt;p&gt;Error displayed when the game is inserted into a system not compatible with the Game Boy Color.&lt;/p&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;As &lt;em&gt;Harry Potter and the Sorcerer&amp;rsquo;s Stone&lt;/em&gt; is a Game Boy Color-only game, the
very first thing it does is check to see if it&amp;rsquo;s running on a Game Boy
Color-compatible system. When it jumps to &lt;code&gt;Start&lt;/code&gt; it compares the value left in
the &lt;code&gt;a&lt;/code&gt; register by the system boot ROM to &lt;code&gt;$11&lt;/code&gt;. If the zero flag is set, it
later jumps to code to show an error message.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Start::
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff0&#34;&gt;and&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;                       &lt;span style=&#34;color:#0f0&#34;&gt;; clear flags
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;cp&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;BOOTUP_A_CGB&lt;/span&gt;             &lt;span style=&#34;color:#0f0&#34;&gt;; is Game Boy Color?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff0&#34;&gt;ld&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#7fffd4&#34;&gt;$00&lt;/span&gt;                   &lt;span style=&#34;color:#0f0&#34;&gt;; set a to 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;jr&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;nz&lt;/span&gt;, &lt;span style=&#34;color:#7fffd4&#34;&gt;.notGBC&lt;/span&gt;              &lt;span style=&#34;color:#0f0&#34;&gt;; if not GBC:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;inc&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;                       &lt;span style=&#34;color:#0f0&#34;&gt;;   increment a (a=1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.notGBC:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff0&#34;&gt;ldh&lt;/span&gt; [&lt;span style=&#34;color:#7fffd4&#34;&gt;$ef&lt;/span&gt;], &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;                &lt;span style=&#34;color:#0f0&#34;&gt;; save GBC value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;ld&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;sp&lt;/span&gt;, &lt;span style=&#34;color:#7fffd4&#34;&gt;$cfff&lt;/span&gt;                &lt;span style=&#34;color:#0f0&#34;&gt;; setup stack pointer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;ldh&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;, [&lt;span style=&#34;color:#7fffd4&#34;&gt;$ef&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff0&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff0&#34;&gt;call&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;z&lt;/span&gt;, &lt;span style=&#34;color:#7fffd4&#34;&gt;Unknown_Non_GBC&lt;/span&gt;     &lt;span style=&#34;color:#0f0&#34;&gt;; call if not GBC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As this code saves the GBC status into High RAM, instead of directly jumping to
code to display the error message, I wonder if at some point in development the
game was targetting compatibility with the earlier Game Boy systems.&lt;/p&gt;
&lt;h3 id=&#34;--floop-flatten&#34;&gt;&amp;ndash;floop-flatten&lt;/h3&gt;
&lt;p&gt;What do you do if you want quickly copy contiguous memory from multiple parts of
the game, but part of the game needs to copy a different number of bytes, and
you also want to keep the number of CPU cycles to a minimum? One approach taken
by the &lt;em&gt;Sorcerer&amp;rsquo;s Stone&lt;/em&gt;&amp;rsquo;s developers is to flatten the loop. You can call the
&lt;a href=&#34;https://github.com/terinjokes/HPSS-Disassembly/blob/dedcb131ea/src/bank_000.asm#L5980-L6168&#34;&gt;same 3 instructions 32
times&lt;/a&gt;,
then call into the code at whatever point has the required number of iterations
remaining.&lt;/p&gt;
&lt;p&gt;Fortunately, &lt;code&gt;rgbasm&lt;/code&gt; is a macro assembler, allowing us to refactor this logic
to generate the assembly for us, while also generating more readable names.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-asm&#34; data-lang=&#34;asm&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff0&#34;&gt;FOR&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;V&lt;/span&gt;, &lt;span style=&#34;color:#f60&#34;&gt;32&lt;/span&gt;, &lt;span style=&#34;color:#f60&#34;&gt;0&lt;/span&gt;, -&lt;span style=&#34;color:#f60&#34;&gt;1&lt;/span&gt;                &lt;span style=&#34;color:#0f0&#34;&gt;; loop from 32 to 0, decrementing each time
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#7fffd4&#34;&gt;CopyHL2DE_&lt;/span&gt;{&lt;span style=&#34;color:#7fffd4&#34;&gt;d&lt;/span&gt;:&lt;span style=&#34;color:#7fffd4&#34;&gt;V&lt;/span&gt;}:                &lt;span style=&#34;color:#0f0&#34;&gt;; generate a label we can reference from other code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;ld&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;, [&lt;span style=&#34;color:#7fffd4&#34;&gt;hl&lt;/span&gt;+]                 &lt;span style=&#34;color:#0f0&#34;&gt;; load the byte pointed to by hl into a,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;                                &lt;span style=&#34;color:#0f0&#34;&gt;;     and also increment hl
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;ld&lt;/span&gt; [&lt;span style=&#34;color:#7fffd4&#34;&gt;de&lt;/span&gt;], &lt;span style=&#34;color:#7fffd4&#34;&gt;a&lt;/span&gt;                  &lt;span style=&#34;color:#0f0&#34;&gt;; load a into the byte referenced by de
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#7fffd4&#34;&gt;inc&lt;/span&gt; &lt;span style=&#34;color:#7fffd4&#34;&gt;de&lt;/span&gt;                      &lt;span style=&#34;color:#0f0&#34;&gt;; increment de
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#7fffd4&#34;&gt;ENDR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This reduced over 200 lines of code into a much more manageable 6!&lt;/p&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve still have a lot of tasks ahead. Some immediate tasks to start working on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Begin extracting tiles into bitmaps, and convert and assemble on-the-fly
during builds.&lt;/li&gt;
&lt;li&gt;Work on extracting text into forms that can be easier to work with, especially
for translators.&lt;/li&gt;
&lt;li&gt;Label and comment even more code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In &lt;a href=&#34;https://kemenaran.winosx.com/posts/special-effects-in-zelda-links-awakening&#34;&gt;the first blog
post&lt;/a&gt;
for disassembling &lt;em&gt;Zelda: Link&amp;rsquo;s Awakening DX&lt;/em&gt;, Pierre writes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Reverse-engineering assembly code is quite slow, but I&amp;rsquo;ll try to post some
findings on this blog.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The statement is just as true here as it was all those years ago. We&amp;rsquo;ll see how
this goes!&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Flandrew must be from outside the United States of America.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/List_of_Game_Boy_Color_games&#34;&gt;According to
Wikipedia&lt;/a&gt; five
games released after &lt;em&gt;Chamber of Secrets&lt;/em&gt; in other markets, 1 in Germany, 1
in Korea, and 3 in Japan, including the last licensed game &lt;em&gt;Doraemon no
Study Boy: Kanji Yomikaki Master&lt;/em&gt;.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </item>
    
    <item>
      <title>Systemd&#39;s clock-epoch for RTC-less systems</title>
      <link>https://terinstock.com/post/2021/12/Systemds-clock-epoch-for-RTC-less-systems/</link>
      <pubDate>Sun, 26 Dec 2021 06:24:00 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2021/12/Systemds-clock-epoch-for-RTC-less-systems/</guid>
      <content type="html">&lt;p&gt;Earlier today I restarted a Raspberry Pi I use on my home network. When the system came up after reboot, many services failed to resume, which I quickly noticed due to the lack of DNS resolution on my network. Upon checking the system logs, the culprit became clear: CoreDNS was failing to verify the certificates of my DNS-over-TLS provider. According to my Raspberry Pi, we were partying like it was 1999. Without DNS resolution the system time would never automatically synchronize with NTP.&lt;/p&gt;
&lt;p&gt;On a Raspberry Pi running Raspian or Raspberry Pi OS, the system is automatically configured to use &lt;code&gt;fake-hwclock&lt;/code&gt; to save the time to disk from a cron, and restore it at boot. However, this package isn&amp;rsquo;t widely distributed outside Debian and it&amp;rsquo;s derivatives.&lt;/p&gt;
&lt;p&gt;At boot, systemd compares the system time to a builtin epoch, usually the release or build date of systemd. If it finds the system time is before this epoch, it resets the clock to the epoch. Unfortunately, this is still sometimes too old, especially when it comes to TLS certificates.&lt;/p&gt;
&lt;p&gt;We can still utilize this systemd feature however, at least if systemd is version 247 or newer, by utilizing a hook added to aid system administrators and image distributors. If the file &lt;code&gt;/usr/lib/clock-epoch&lt;/code&gt; exists, systemd uses the modified time as the systemd epoch.&lt;/p&gt;
&lt;p&gt;We can even script periodically updating this file. First, create &lt;code&gt;/etc/systemd/system/set-clock-epoch.service&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Description=&lt;span style=&#34;color:#87ceeb&#34;&gt;Updates the mtime of clock-epoch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ExecStart=&lt;span style=&#34;color:#87ceeb&#34;&gt;/bin/touch -m /usr/lib/clock-epoch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, create a timer to start it periodically, at &lt;code&gt;/etc/systemd/system/set-clock-epoch.timer&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Description=&lt;span style=&#34;color:#87ceeb&#34;&gt;Timer for updating clock-epoch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;[Timer]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OnBootSec=&lt;span style=&#34;color:#87ceeb&#34;&gt;5min&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OnUnitInactiveSec=&lt;span style=&#34;color:#87ceeb&#34;&gt;17min&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;WantedBy=&lt;span style=&#34;color:#87ceeb&#34;&gt;timers.target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve arbitrarily delayed activation of this timer by setting &lt;code&gt;OnBootSec&lt;/code&gt;, since this is low priority at boot time.&lt;/p&gt;
&lt;p&gt;Then reload the daemon, then enable and start the timer.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;systemctl enable --now set-clock-epoch.timer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when the system reboots, systemd will read the modification time of this file during early boot, setting the time to a reasonably close value, allowing certificates to be validated and NTP to take over.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;As &lt;a href=&#34;https://lobste.rs/s/0jlh6q/systemd_s_clock_epoch_for_rtc_less_systems#c_exj4qa&#34;&gt;gioele&lt;/a&gt; on Lobste.rs points out, on systemd version 250 and later, if you&amp;rsquo;re using &lt;code&gt;systemd-timesyncd&lt;/code&gt; you can set &lt;code&gt;SaveIntervalSec=&lt;/code&gt; to automatically write the time out to the filesystem. This follows a similar mechanism, the file &lt;code&gt;/var/lib/systemd/timesync/clock&lt;/code&gt; has it&amp;rsquo;s modification time set, and is used to restore out reboot. The time is set by the epoch mechanism much earlier in the startup process than the timesyncd mechanism (which first requires service activation to begin). It also doesn&amp;rsquo;t help if, like me, you&amp;rsquo;re not using timesyncd.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Clearing Custom Search Engines in Chrome</title>
      <link>https://terinstock.com/post/2021/08/Clearing-Custom-Search-Engines-in-Chrome/</link>
      <pubDate>Wed, 18 Aug 2021 09:20:00 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2021/08/Clearing-Custom-Search-Engines-in-Chrome/</guid>
      <content type="html">&lt;p&gt;In addition to your search engine in Google Chrome, the browser tries to be
helpful by remembering &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/OpenSearch#autodiscovery_of_search_plugins&#34;&gt;custom search engines&lt;/a&gt; for websites you
visit in the course of navigating the web. As some websites can become
registered simply by navigating to their homepage, this can result in a very
long list of custom search engines.&lt;/p&gt;
&lt;p&gt;Unfortunately, the page to manage custom search engines in Google Chrome
(currently located at &lt;code&gt;chrome://settings/searchEngines&lt;/code&gt;) leaves a lot to be
desired. This page has no mechanism for bulk editing. With over two hundred
engines registered, I did not want to click my mouse to manually manage them.&lt;/p&gt;
&lt;p&gt;Fortunately, we can activate super developer powers!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;const&lt;/span&gt; nl = document.querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;settings-ui&amp;#34;&lt;/span&gt;).shadowRoot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;#main&amp;#34;&lt;/span&gt;).shadowRoot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;[role=main]&amp;#34;&lt;/span&gt;).shadowRoot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;settings-search-page&amp;#34;&lt;/span&gt;).shadowRoot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;settings-search-engines-page&amp;#34;&lt;/span&gt;).shadowRoot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;#otherEngines&amp;#34;&lt;/span&gt;).shadowRoot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .querySelectorAll(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;settings-search-engine-entry&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ah, right. So, the settings pages uses &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM&#34;&gt;Shadow DOM&lt;/a&gt;, which makes
getting to the actual list of custom search engines a bit of a chore. Query
through a bunch of document fragments to get to the actual list.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;const&lt;/span&gt; ops = Array.from(nl).map((elem) =&amp;gt; elem.shadowRoot)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .filter((elem) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f00&#34;&gt;return&lt;/span&gt; !elem.querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;#keyword-column&amp;#34;&lt;/span&gt;).innerText.startsWith(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .map((elem) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f00&#34;&gt;return&lt;/span&gt; [elem.querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;#keyword-column&amp;#34;&lt;/span&gt;).innerText, elem.querySelector(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;#delete&amp;#34;&lt;/span&gt;)];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;console.table(ops);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I use the custom search engine feature to implement DuckDuckGo-style &lt;a href=&#34;https://duckduckgo.com/bang&#34;&gt;!Bang
searches&lt;/a&gt;. So before removing engines, I filter out items starting with
&amp;ldquo;!&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;If the table printed to the console looks good, click the virtual mouse.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ops.forEach(entry =&amp;gt; entry[&lt;span style=&#34;color:#f60&#34;&gt;1&lt;/span&gt;].click());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
    </item>
    
    <item>
      <title>Setting up a git server on NixOS</title>
      <link>https://terinstock.com/post/2021/01/Setting-up-a-git-server-on-NixOS/</link>
      <pubDate>Wed, 27 Jan 2021 21:23:25 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2021/01/Setting-up-a-git-server-on-NixOS/</guid>
      <content type="html">&lt;p&gt;Over the years I&amp;rsquo;ve tried out many VPS providers, which has resulted in personal
infrastructure spread far and wide. I&amp;rsquo;ve decided to start 2021 by consolidating
this infrastructure, where reasonable. This also gives me an opportunity to
properly document deployments, ideally making them easy to redeploy in the
future. NixOS allows me to solve both goals relatively easily.&lt;/p&gt;
&lt;p&gt;One piece of personal infrastructure is a git server, &lt;a href=&#34;https://git.terinstock.com&#34;&gt;git.terinstock.com&lt;/a&gt;,
which I use for personal configuration or smaller projects. Since I&amp;rsquo;m the only
user, I don&amp;rsquo;t need the fancy interfaces provided by options like &lt;a href=&#34;https://gogs.io/&#34;&gt;gogs&lt;/a&gt;,
&lt;a href=&#34;https://gitea.io/&#34;&gt;gitea&lt;/a&gt;, or &lt;a href=&#34;https://about.gitlab.com/features/&#34;&gt;GitLab&lt;/a&gt;. I&amp;rsquo;ve chosen to use cgit, which had a few
more features I wanted over &lt;a href=&#34;https://git-scm.com/book/en/v2/Git-on-the-Server-GitWeb&#34;&gt;gitweb&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It did feel a little strange to deploy a cgi server in 2021. I think we call
this &amp;ldquo;serverless&amp;rdquo; now?&lt;/p&gt;
&lt;h2 id=&#34;cgit&#34;&gt;cgit&lt;/h2&gt;
&lt;p&gt;NixOS already has cgit packaged, so we can start with configuring it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;let&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cgit = pkgs.cgit;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  cgitConfig = pkgs.writeText &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;cgitrc&amp;#34;&lt;/span&gt; (lib.generators.toKeyValue { } {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    css = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/cgit.css&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    logo = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/cgit.png&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    favicon = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/favicon.ico&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    about-filter = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;cgit&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/lib/cgit/filters/about-formatting.sh&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    source-filter = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;cgit&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/lib/cgit/filters/syntax-highlighting.py&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    clone-url = (lib.concatStringsSep &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;https://$HTTP_HOST$SCRIPT_NAME/$CGIT_REPO_URL&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ssh://git@git.terinstock.com:$CGIT_REPO_URL&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    enable-log-filecount = &lt;span style=&#34;color:#f60&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    enable-log-linecount = &lt;span style=&#34;color:#f60&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    enable-git-config = &lt;span style=&#34;color:#f60&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root-title = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;git.terinstock.com&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    root-desc = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;Terin&amp;#39;s Git Repositories&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scan-path = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/srv/git&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This assigns a derivation to &lt;code&gt;cgitConfig&lt;/code&gt; that when evaluated would create the
ini-like configuration file. Most of this comes down to personal preference with
a few exceptions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;about-filter&lt;/code&gt; and &lt;code&gt;source-filter&lt;/code&gt; reference the respective filters wpithin
the cgit package. NixOS will expand these to full paths when creating the
configuration file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scan-path&lt;/code&gt; is the location on disk I&amp;rsquo;m using to host the git repositories.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;git-shell&#34;&gt;git-shell&lt;/h2&gt;
&lt;p&gt;To allow for authenticated pushes, I use &lt;code&gt;git-shell&lt;/code&gt; provided by the &lt;code&gt;git&lt;/code&gt;
project. This provides a minimal &amp;ldquo;shell&amp;rdquo; that an execute a prescribed list of
git commands. I define a system user &lt;code&gt;git&lt;/code&gt; and assign it this shell.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  users.users.git = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    isSystemUser = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;git user&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    home = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/srv/git&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shell = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.git&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/bin/git-shell&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    openssh.authorizedKeys.keys = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIINNSIl3/j3KqW/x3kFj1ZvZlSxp+MDhk8LAIDlqs/9w&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As I&amp;rsquo;m the only user that will be pushing to these repositories, I don&amp;rsquo;t need to
configure anything extra for authenticating actions.&lt;/p&gt;
&lt;h2 id=&#34;web-server-h2o&#34;&gt;Web Server, h2o&lt;/h2&gt;
&lt;p&gt;Since cgit is a cgi application, it needs a web server to actually handle the
HTTP connections. I&amp;rsquo;ve chosen to use &lt;a href=&#34;https://h2o.examp1e.net/&#34;&gt;h2o&lt;/a&gt; as it supports being an
application proxy in addition to serving static files, while still being
configurable with YAML/JSON.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;let&lt;/span&gt; h2oFile = file: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;file.file&amp;#34;&lt;/span&gt; = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;file&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;file.send-compressed&amp;#34;&lt;/span&gt; = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ON&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  h2oHeaderList = attrs: (lib.mapAttrsToList (k: v: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;k&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;: &lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;v&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;) attrs);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  h2oConfig = pkgs.writeText &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;h2o.conf&amp;#34;&lt;/span&gt; (lib.generators.toYAML { } {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    hosts.&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;git.terinstock.com&amp;#34;&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      paths = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/cgit.css&amp;#34;&lt;/span&gt; = h2oFile &lt;span style=&#34;color:#87ceeb&#34;&gt;./cgit.css&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/cgit.png&amp;#34;&lt;/span&gt; = h2oFile &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;cgit&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/cgit/cgit.png&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/favicon.ico&amp;#34;&lt;/span&gt; = h2oFile &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;cgit&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/cgit/favicon.ico&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/robots.txt&amp;#34;&lt;/span&gt; = h2oFile &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;cgit&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/cgit/robots.txt&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;fastcgi.spawn&amp;#34;&lt;/span&gt; = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.h2o&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/share/h2o/fastcgi-cgi&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          setenv = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            SCRIPT_FILENAME = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;cgit&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/cgit/cgit.cgi&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            CGIT_CONFIG = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;cgitConfig&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          compress = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ON&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;header.set&amp;#34;&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        header = (h2oHeaderList {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          x-frame-options = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;deny&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          x-xss-protection = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;1, mode=block&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          x-content-type-options = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;nosniff&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          referrer-policy = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;no-referrer, strict-origin-when-cross-origin&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          cache-control = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;no-transform&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          strict-transport-security = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;max-age=63072000&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          content-security-policy = (lib.concatStringsSep &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;; &amp;#34;&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;default-src &amp;#39;none&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;style-src &amp;#39;self&amp;#39; &amp;#39;unsafe-inline&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;img-src &amp;#39;self&amp;#39; data: https://img.shields.io&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;script-src-attr &amp;#39;unsafe-inline&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          expect-ct = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;enforce, max-age=30&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      listen = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        port = &lt;span style=&#34;color:#f60&#34;&gt;443&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ssl = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          certificate-file = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/acme/git.terinstock.com/fullchain.pem&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          key-file = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/acme/git.terinstock.com/key.pem&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          min-version = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;TLSv1.2&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          cipher-preference = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          cipher-suite = (lib.concatStringsSep &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;:&amp;#34;&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;TLS_AES_128_GCM_SHA256&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;TLS_AES_256_GCM_SHA384&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;TLS_CHACHA20_POLY1305_SHA256&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-ECDSA-AES128-GCM-SHA256&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-RSA-AES128-GCM-SHA256&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-ECDSA-AES256-GCM-SHA384&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-RSA-AES256-GCM-SHA384&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-ECDSA-CHACHA20-POLY1305&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-RSA-CHACHA20-POLY1305&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;DHE-RSA-AES128-GCM-SHA256&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;DHE-RSA-AES256-GCM-SHA384&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Like with cgit&amp;rsquo;s configuration, &lt;code&gt;h2oConfig&lt;/code&gt; is assigned a derivation that when
evaluated will create a configuration file for h2o. In this configuration one
host is defined and paths are defined for static assets, A fastcgi handler is
configured for the root, which will be used for all paths not matched by a
static file path.&lt;/p&gt;
&lt;p&gt;h2o does not support cgi directly, but ships with a script to proxy through a
fastcgi server. This wrapper is configured with the path to cgit&amp;rsquo;s and the cgit
configuration file defined earlier.&lt;/p&gt;
&lt;p&gt;This is more verbose than strictly neccessary, as I wanted to have high marks on
Qualys&amp;rsquo;s &lt;a href=&#34;https://www.ssllabs.com/index.html&#34;&gt;SSL Labs&lt;/a&gt; report and &lt;a href=&#34;https://securityheaders.com/&#34;&gt;Security Headers&lt;/a&gt;. I
imagine in the future I&amp;rsquo;ll have refactored this into smaller functions and
options.&lt;/p&gt;
&lt;p&gt;The documented syntax for adding response headers involves setting the
&lt;code&gt;headers.set&lt;/code&gt; key multiple times. This is not representable in Nix, as Nix will
not allow the same key to be set multiple times. An undocumented syntax using a
YAML sequence instead has been available since 2.3.0-rc2.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;header.set:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    header:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;x-frame-options: \&amp;#34;deny\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;x-xss-protection: \&amp;#34;1, mode=block\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, I we need to start the h2o web server. At the time of writing, h2o does
not have a NixOS module, but we can use lower-level modules ourselves.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  systemd.services.h2o = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;H2O web server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    after = [ &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;network-online.target&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;acme-git.terinstock.com.service&amp;#34;&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wantedBy = [ &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;multi-user.target&amp;#34;&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path = [ pkgs.perl pkgs.openssl ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    serviceConfig = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ExecStart = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.h2o&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/bin/h2o --mode master --conf &lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;h2oConfig&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ExecReload = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.coreutils&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/bin/kill -s HUP $MAINPID&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ExecStop = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.coreutils&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/bin/kill -s QUIT $MAINPID&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      User = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;h2o&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Group = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;h2o&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Type = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;simple&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Restart = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;on-failure&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      AmbientCapabilities = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;cap_net_bind_service&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      CapabilitiesBoundingSet = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;cap_net_bind_service&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      NoNewPrivileges = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      LimitNPROC = &lt;span style=&#34;color:#f60&#34;&gt;64&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      LimitNOFILE = &lt;span style=&#34;color:#f60&#34;&gt;1048576&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      PrivateDevices = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      PrivetTmp = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ProtectHome = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ProtectSystem = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;full&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ReadOnlyPaths = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/acme/&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ReadWriteDirectories = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/h2o&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This configures a systemd unit that will be enabled at startup. The &lt;code&gt;ExecStart&lt;/code&gt;
will start h2o without daemonizing, and provides the path to the evaluated h2o
configuration. The &lt;code&gt;PATH&lt;/code&gt; environment variable for the service will be updated
to include Perl (for the fastcgi proxy) and OpenSSL (for OCSP stapling). I may
submit a pull request to nixpkgs to fix the h2o derivation to reference these
directly.&lt;/p&gt;
&lt;p&gt;Additional settings are provided to the unit to configure systemd&amp;rsquo;s sandbox for
the service.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  users.users.h2o = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    group = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;h2o&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    home = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/h2o&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    createHome = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    isSystemUser = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  users.groups.h2o = { };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another system user is created, this time for h2o, along with a correspondng
group used shortly.&lt;/p&gt;
&lt;h2 id=&#34;acme-certificates&#34;&gt;ACME Certificates&lt;/h2&gt;
&lt;p&gt;NixOS ships with great support for using ACME to create TLS certificates, such
as from Let&amp;rsquo;s Encrypt or &lt;a href=&#34;https://terinstock.com/post/2021/01/Pebble-and-lego-to-test-ACME-with-NixOS/&#34;&gt;your own certificate authority&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  security.acme = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#0f0&#34;&gt;# Set to true if you agree to your ACME server&amp;#39;s Terms of Service.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#0f0&#34;&gt;# For Let&amp;#39;s Encrypt: https://letsencrypt.org/repository/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    acceptTerms = &lt;span style=&#34;color:#7fffd4&#34;&gt;false&lt;/span&gt;; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;terinjokes@gmail.com&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    certs = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;git.terinstock.com&amp;#34;&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        dnsProvider = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;cloudflare&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        credentialsFile = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/secrets/cloudflare.secret&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        group = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;h2o&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To avoiding needing to configure h2o to proxy to lego for HTTP challenges, I
prefer to use the DNS ACME challenges with lego&amp;rsquo;s support for &lt;a href=&#34;https://go-acme.github.io/lego/dns/cloudflare/&#34;&gt;Cloudflare
DNS&lt;/a&gt;. Configuring lego to do DNS challenges is outside the scope of this
post.&lt;/p&gt;
&lt;h2 id=&#34;housekeeping&#34;&gt;Housekeeping&lt;/h2&gt;
&lt;p&gt;I configure a few more NixOS options for general housekeeping that don&amp;rsquo;t fit in
the above sections.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  environment.systemPackages = [ pkgs.git ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I install git as a system package so it&amp;rsquo;s avialable should I need to log into
the box to handle something.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  services.openssh.passwordAuthentication = &lt;span style=&#34;color:#7fffd4&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  services.sshguard.enable = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I disable OpenSSH&amp;rsquo;s password authentication mechanisms, as I have a strong
preference to using more secure options. I also enable &lt;a href=&#34;https://www.sshguard.net/&#34;&gt;sshguard&lt;/a&gt; to
block connections that try to log in with a password anyways.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  nix.gc = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    automatic = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    options = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;--delete-older-than 30d&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  nix.optimise.automatic = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  system.autoUpgrade = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    enable = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    allowReboot = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Configures NixOS to perform routine maintaince in the background. This allows
for NixOS to update at a regular interval, rebooting if needed to install kernel
updates, as well as optimizing and garbage collecting the nix store.&lt;/p&gt;
&lt;p&gt;While the goal in 2021 is to have less machines to manage, this ensures I don&amp;rsquo;t
forget to install security patches because I haven&amp;rsquo;t logged in for a while.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Pebble and lego to test ACME with NixOS</title>
      <link>https://terinstock.com/post/2021/01/Pebble-and-lego-to-test-ACME-with-NixOS/</link>
      <pubDate>Thu, 14 Jan 2021 22:04:00 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2021/01/Pebble-and-lego-to-test-ACME-with-NixOS/</guid>
      <content type="html">&lt;p&gt;When configuring a new service I want to run on NixOS, I often use
&lt;a href=&#34;https://github.com/Mic92/nixos-shell&#34;&gt;nixos-shell&lt;/a&gt; to quickly standup a temporary VM locally with my new
configuration. When this configuration is a service, I often want to ensure I
have TLS configured properly, including the correct permissions on the
certificate and key files managed by &lt;a href=&#34;https://go-acme.github.io/lego/&#34;&gt;lego&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, I don&amp;rsquo;t neccessarily want to send unacceptable amounts of traffic to
production ACME servers, or deal with proper validation with their staging
services. Fortunately, we can configure the NixOS VM to start a testing ACME
server, &lt;a href=&#34;https://github.com/letsencrypt/pebble&#34;&gt;Pebble&lt;/a&gt;, and configure Lego to use it.&lt;/p&gt;
&lt;h2 id=&#34;pebble&#34;&gt;Pebble&lt;/h2&gt;
&lt;p&gt;Pebble is a small, single-binary ACME server intended for testing. Keys and
certificates are randomnized between calls, but this is fine for an emphermial
VM.&lt;/p&gt;
&lt;p&gt;First, we&amp;rsquo;ll want to configure Pebble to start, which we can do with the
&lt;code&gt;systemd.service&lt;/code&gt; NixOS option. I use the &lt;code&gt;toJSON&lt;/code&gt; builtin function to create a
JSON configuration file for Pebble from a Nix attribute set. I also reference
the default certificate and key from the source package, as they are not yet
copied to the output package.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ pkgs }:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;let&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  pebbleConfig = pkgs.writeText &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;pebble.json&amp;#34;&lt;/span&gt; (builtins.toJSON {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pebble = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      listenAddress = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;0.0.0.0:14000&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      managementListenAddress = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;0.0.0.0:15000&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      certificate = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.pebble.src&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/test/certs/localhost/cert.pem&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      privateKey = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.pebble.src&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/test/certs/localhost/key.pem&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      httpPort = &lt;span style=&#34;color:#f60&#34;&gt;5002&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      tlsPort = &lt;span style=&#34;color:#f60&#34;&gt;5001&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ocspResponderURL = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      externalAccountBindingRequired = &lt;span style=&#34;color:#7fffd4&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;in&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  systemd.services.pebble = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    description = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;Pebble ACME Test Server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    after = [ &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;network-online.target&amp;#34;&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    wantedBy = [ &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;multi-user.target&amp;#34;&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    serviceConfig = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      Environment = [ &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;PEBBLE_VA_NOSLEEP=1&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;PEBBLE_VA_ALWAYS_VALID=1&amp;#34;&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ExecStart = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.pebble&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/bin/pebble -config &lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pebbleConfig&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      DynamicUser = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can modify some behavior of Pebble through environment variables. I set two
for better behavior in a development VM:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PEBBLE_VA_NOSLEEP&lt;/code&gt; disables any artifical sleeps in the issuance path, as
we&amp;rsquo;re not interested in testing lego&amp;rsquo;s validationg polling.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PEBBLE_VA_ALWAYS_VALID&lt;/code&gt; disables all validation methods, and assumes domains
have already been successfully validated.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One warning from the Pebble documentation bares repeating here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pebble is &lt;strong&gt;NOT INTENDED FOR PRODUCTION USE&lt;/strong&gt;. Pebble is for &lt;strong&gt;testing only&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;lego&#34;&gt;lego&lt;/h2&gt;
&lt;p&gt;lego is a widely used ACME client that implements all of the ACME challenges,
bindings for major DNS providers, and support for bundling certificates. It&amp;rsquo;s
used as the implementation to NixOS&amp;rsquo;s &lt;code&gt;security.acme&lt;/code&gt; options. This means most
of the configuration is already done for us.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  security.acme = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    server = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;https://localhost:14000/dir&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    acceptTerms = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    email = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;webmaster@example.com&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    certs = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        webroot = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/www/example.com&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        group = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;prosody&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Despite not needing to implement any challenges, as we&amp;rsquo;ve disabled them in
Pebble, we still need to provide &lt;code&gt;webroot&lt;/code&gt; configuration. The &lt;code&gt;group&lt;/code&gt; attribute
is used to set the group permission on the generated certificates and keys, and
should be set to the same user your server is running as.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s one farther complication: lego does not trust the certificate authority
used by the ACME server, and thus it won&amp;rsquo;t issue requests out of the box. We can
configure the lego to trust these certificates by setting the environment of the
generated service unit.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  systemd.services.&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;acme-example.com&amp;#34;&lt;/span&gt;.serviceConfig.Environment = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;LEGO_CA_CERTIFICATES=&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;${&lt;/span&gt;pkgs.pebble.src&lt;span style=&#34;color:#87ceeb&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#87ceeb&#34;&gt;/test/certs/pebble.minica.pem&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;LEGO_CA_SERVER_NAME=localhost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;usage&#34;&gt;Usage&lt;/h2&gt;
&lt;p&gt;The certificates and keys created by &lt;code&gt;security.acme&lt;/code&gt; are stored underneath
&lt;code&gt;/var/lib/acme/&lt;/code&gt;. This can be provided to your server&amp;rsquo;s configuration.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  services.prosody = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    enable = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    virtualHosts.&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt; = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      enabled = &lt;span style=&#34;color:#7fffd4&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ssl.cert = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/acme/git.terinstock.com/fullchain.pem&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ssl.key = &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/var/lib/acme/git.terinstock.com/key.pem&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
    </item>
    
    <item>
      <title>K9P: Kubernetes as 9P Files</title>
      <link>https://terinstock.com/talk/2019/11/K9P-Kubernetes-as-9P-Files/</link>
      <pubDate>Wed, 20 Nov 2019 11:55:00 -0800</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/talk/2019/11/K9P-Kubernetes-as-9P-Files/</guid>
      <content type="html">&lt;p&gt;K9P is a virtual filesystem that allows you to interact with Kubernetes clusters via a 9P filesystem, allowing us
to continue to believe that everything is a file. Presented at &lt;a href=&#34;https://events19.linuxfoundation.org/events/kubecon-cloudnativecon-north-america-2019/&#34;&gt;KubeCon + CloudNativeCon North America 2019&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;iframe id=&#34;talk_frame_577573&#34; class=&#34;speakerdeck-iframe&#34; src=&#34;//speakerdeck.com/player/afbb112c263744a39d2432e9eb91289c&#34; width=&#34;710&#34; height=&#34;399&#34; style=&#34;aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;&#34; frameborder=&#34;0&#34; allowtransparency=&#34;true&#34; allowfullscreen=&#34;allowfullscreen&#34;&gt;&lt;/iframe&gt;


&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/qykUb8-Nxxw&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Building a Hardware MIDI Player</title>
      <link>https://terinstock.com/talk/2019/02/Building-a-Hardware-MIDI-Player/</link>
      <pubDate>Sat, 02 Feb 2019 15:30:00 +0100</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/talk/2019/02/Building-a-Hardware-MIDI-Player/</guid>
      <content type="html">&lt;p&gt;Building a MIDI player using Go and various Linux system interfaces, presented at &lt;a href=&#34;https://fosdem.org/2019/schedule/&#34;&gt;FOSDEM 2019&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;iframe id=&#34;talk_frame_491828&#34; class=&#34;speakerdeck-iframe&#34; src=&#34;//speakerdeck.com/player/cd4d70fe6fd84990ab11a32730b0126f&#34; width=&#34;710&#34; height=&#34;399&#34; style=&#34;aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;&#34; frameborder=&#34;0&#34; allowtransparency=&#34;true&#34; allowfullscreen=&#34;allowfullscreen&#34;&gt;&lt;/iframe&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://iframe.cloudflarestream.com/f16388f46fc1d28d8143470b03ae8e3b&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%;&#34; allowfullscreen frameboarder=&#34;0&#34; title=&#34;Cloudflare Stream Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>memfd_create: Temporary in-memory files with Go and Linux</title>
      <link>https://terinstock.com/post/2018/10/memfd_create-Temporary-in-memory-files-with-Go-and-Linux/</link>
      <pubDate>Sat, 20 Oct 2018 23:21:27 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2018/10/memfd_create-Temporary-in-memory-files-with-Go-and-Linux/</guid>
      <content type="html">&lt;p&gt;My &lt;a href=&#34;https://terinstock.com/post/2018/09/Hardware-MIDI-Player-Part-One/&#34;&gt;MIDI Player&lt;/a&gt; project requires interfacing with libraries and system calls
that don&amp;rsquo;t yet have Go equivalents. A few of these C libraries expect to be
invoked with file paths or file descriptors.&lt;/p&gt;
&lt;p&gt;If the data is already available as a file on a mounted disk, this is fine,
I can pass the path or descriptor that already exists. I run into a problem,
however, as soon as I want to work with data that isn&amp;rsquo;t directly backed by
a file, such as after being manipulated by the program, or having been unpacked
from a container format.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a simple way to get a file path: create a temporary file somewhere.
This requires having a writable filesystem mounted, and cleaning up the files
when no longer required. On a device that&amp;rsquo;s intended to be used as an
appliance, I don&amp;rsquo;t have anywhere writable.&lt;/p&gt;
&lt;p&gt;In Linux 3.17 and later, I have another option &lt;a href=&#34;https://jlk.fjfi.cvut.cz/arch/manpages/man/core/man-pages/memfd_create.2.en&#34;&gt;&lt;code&gt;memfd_create(2)&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;memfd_create() creates an anonymous file and returns a file descriptor that
refers to it. The file behaves like a regular file, and so can be modified,
truncated, memory-mapped, and so on. However, unlike a regular file, it lives
in RAM and has a volatile backing storage. Once all references to the file are
dropped, it is automatically released.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This checks the boxes for most of my requirements: a file descriptor to
something that acts like a normal file, backed by memory, doesn&amp;rsquo;t require any
writable mount points, and automatically cleans up files when closed.&lt;/p&gt;
&lt;p&gt;The system call is available in the Go &lt;a href=&#34;https://godoc.org/golang.org/x/sys/unix&#34;&gt;&lt;code&gt;x/sys/unix&lt;/code&gt;&lt;/a&gt; package, along with
a few companion calls.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;golang.org/x/sys/unix&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;// memfile takes a file name used, and the byte slice
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;// containing data the file should contain.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;// name does not need to be unique, as it&amp;#39;s used only
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;// for debugging purposes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;// It is up to the caller to close the returned descriptor.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#f00&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#ff0&#34;&gt;memfile&lt;/span&gt;(name &lt;span style=&#34;color:#ee82ee&#34;&gt;string&lt;/span&gt;, b []&lt;span style=&#34;color:#ee82ee&#34;&gt;byte&lt;/span&gt;) (&lt;span style=&#34;color:#ee82ee&#34;&gt;int&lt;/span&gt;, &lt;span style=&#34;color:#ee82ee&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	fd, err := unix.&lt;span style=&#34;color:#ff0&#34;&gt;MemfdCreate&lt;/span&gt;(name, &lt;span style=&#34;color:#f60&#34;&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f00&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f60&#34;&gt;0&lt;/span&gt;, fmt.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;MemfdCreate: %v&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	err = unix.&lt;span style=&#34;color:#ff0&#34;&gt;Ftruncate&lt;/span&gt;(fd, int64(len(b)))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f00&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f60&#34;&gt;0&lt;/span&gt;, fmt.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;Ftruncate: %v&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	data, err := unix.&lt;span style=&#34;color:#ff0&#34;&gt;Mmap&lt;/span&gt;(fd, &lt;span style=&#34;color:#f60&#34;&gt;0&lt;/span&gt;, len(b), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f00&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f60&#34;&gt;0&lt;/span&gt;, fmt.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;Mmap: %v&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	copy(data, b)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	err = unix.&lt;span style=&#34;color:#ff0&#34;&gt;Munmap&lt;/span&gt;(data)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#f00&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f60&#34;&gt;0&lt;/span&gt;, fmt.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;Munmap: %v&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;return&lt;/span&gt; fd, &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some libraries require a file path instead of a file descriptor. Fortunately,
we can turn a file descriptor into a path by way of the proc filesystem using
the symlinks in the /proc/self/fd directory.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;os&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#ff0&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	fd, err != &lt;span style=&#34;color:#ff0&#34;&gt;memfile&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;, []byte(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;hello world!&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		log.&lt;span style=&#34;color:#ff0&#34;&gt;Fatalf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;memfile: %v&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// filepath to our newly created in-memory file descriptor
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	fp := fmt.&lt;span style=&#34;color:#ff0&#34;&gt;Sprintf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/proc/self/fd/%d&amp;#34;&lt;/span&gt;, fd)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// create an *os.File, should you need it
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// alternatively, pass fd or fp as input to a library.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	f := os.&lt;span style=&#34;color:#ff0&#34;&gt;NewFile&lt;/span&gt;(uintptr(fd), fp)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;defer&lt;/span&gt; f.&lt;span style=&#34;color:#ff0&#34;&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Eventually, I&amp;rsquo;ll replace these C libraries with Go packages that operate on
&lt;code&gt;io.Reader&lt;/code&gt;, and this workaround will become unnecessary. Until then, I&amp;rsquo;m glad
the option is available.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Hardware MIDI Player (Part One)</title>
      <link>https://terinstock.com/post/2018/09/Hardware-MIDI-Player-Part-One/</link>
      <pubDate>Mon, 17 Sep 2018 00:35:55 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2018/09/Hardware-MIDI-Player-Part-One/</guid>
      <content type="html">&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;
&lt;figure&gt;&lt;a href=&#34;https://terinstock.com/media/fc/f0760ee810f82952727a4b8afd83dc4d3cf962ef4b993ce71df61163ea0f1a.jpeg&#34;&gt;&lt;img src=&#34;https://terinstock.com/media/fc/f0760ee810f82952727a4b8afd83dc4d3cf962ef4b993ce71df61163ea0f1a:800.jpeg&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;h4&gt;Raspberry Pi MIDI Player&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Last December, I watched Techmoan&amp;rsquo;s YouTube video on the Roland
&lt;a href=&#34;https://www.youtube.com/watch?v=5ks3ucumilU&#34;&gt;MT-80S&lt;/a&gt;, a player built to help teach people how to play songs, that
used HD 3.5&amp;quot; floppy disks as storage medium for MIDI files. Since then AkBKukU
has done a video on the &lt;a href=&#34;https://www.youtube.com/watch?v=O-6xUhR9JAY&#34;&gt;Yamaha Disk Orchestra DOU-10&lt;/a&gt;, that used DD
disks instead.&lt;/p&gt;
&lt;p&gt;As I enjoy listening to chiptune and DOS-era video game music, I was keen on
having a player in my collection. Cursory searches for players sold online
resulted in typically high eBay prices.  Considering my growth in ability with
electronics in the past year, I decided to forgo the market  and build one
myself.&lt;/p&gt;
&lt;p&gt;The vision in my mind belongs in the mid-90s. My completed player shouldn&amp;rsquo;t
feel out of place  alongside a tape deck and CD changer. The insides, however,
should be built from modern components.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&#34;hardware&#34;&gt;Hardware&lt;/h2&gt;
&lt;figure&gt;&lt;a href=&#34;https://terinstock.com/media/e9/5e117f6004dcf0ad422aa25aa93bd74d2d974769ee78df42b121051302b5fe.jpeg&#34;&gt;&lt;img src=&#34;https://terinstock.com/media/e9/5e117f6004dcf0ad422aa25aa93bd74d2d974769ee78df42b121051302b5fe:800.jpeg&#34;
         alt=&#34;Raspberry Pi hidden beneath floppy drive and mess of wires.&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;I began with the internal hardware, which is somewhere underneath all those
wires.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve built the project using Raspberry Pi 3B. This is significantly more
powerful than required for playing back MIDI files.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://terinstock.com/media/3d/fa3360ffc02155f7d82a81408d3d6e00641ff19807be2f3a82a891ce29f627.jpeg&#34;&gt;&lt;img src=&#34;https://terinstock.com/media/3d/fa3360ffc02155f7d82a81408d3d6e00641ff19807be2f3a82a891ce29f627:800.jpeg&#34;
         alt=&#34;Two 3.5-inch floppy drives stacked on top of each other, disconnected.&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;In fact, I had originally planned to use an Arduino Uno as a rudimentary
central processor and floppy drive controller. However, I was unable to
reliably control any of the floppy drives shown here—they might not even work
properly—and decided to switch to using a USB floppy drive that I could more
easily verify. I switched to the Raspberry Pi because it is a better USB host
than an Arduino.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://terinstock.com/media/36/4a627e8905d0224e06f929b7e62c70e1dace1afed6ceb9ea62c6fdd68976b9.jpeg&#34;&gt;&lt;img src=&#34;https://terinstock.com/media/36/4a627e8905d0224e06f929b7e62c70e1dace1afed6ceb9ea62c6fdd68976b9:800.jpeg&#34;
         alt=&#34;USB floppy drive, on desk.&#34;/&gt;&lt;/a&gt;&lt;figcaption&gt;
            &lt;h4&gt;Maccally USB Floppy Disk Drive for Mac&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I settled on  a rather generic USB floppy drive. Well, not just any USB floppy
drive, but one &amp;ldquo;for Mac&amp;rdquo;. Based on the stripe design, I&amp;rsquo;m guessing it was meant
to pair (or, pear) with the fruit-colored iBook G3 clamshells. Fortunately, the
plastic case comes off simply enough.&lt;/p&gt;
&lt;p&gt;This choice of drive proved to be troublesome as I would soon find myself on
a pretty significant detour as a result.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;https://terinstock.com/media/89/cc0b0a19f6427d53fdf3c4576b12c47c927a096c0cd1fe6da52d8b4e75a064.jpeg&#34;&gt;&lt;img src=&#34;https://terinstock.com/media/89/cc0b0a19f6427d53fdf3c4576b12c47c927a096c0cd1fe6da52d8b4e75a064:800.jpeg&#34;
         alt=&#34;Comoponents setting on desk, Dreamblaster S2, Protoboard, Raspberry Pi, and quarter-inch jack&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;I used the &lt;a href=&#34;https://www.amazon.com/MakerSpot-Raspberry-Protoboard-Breadboard-Prototyping/dp/B01M3SI88S&#34;&gt;Protoboard&lt;/a&gt; hat from MakerSpot for the remainder of my
hardware work.  The Pi&amp;rsquo;s serial data and power lines are connected through to
a 90s-era &lt;a href=&#34;http://members.home.nl/c.kersten/&#34;&gt;WaveBlaster-compatible&lt;/a&gt; 2x13&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt; header. I&amp;rsquo;m used an
absolutely tiny &lt;a href=&#34;https://www.serdashop.com/waveblaster&#34;&gt;Dreamblaster S2&lt;/a&gt; as a modern General MIDI synth.
The output from the synthesis chip, a SAM2695, was routed to the Pi&amp;rsquo;s 3.5mm AV
jack. A proper quarter-inch jack has been ordered.&lt;/p&gt;
&lt;h2 id=&#34;software&#34;&gt;Software&lt;/h2&gt;
&lt;p&gt;As a software guy, I expected the hardware to be the hard part. As Caesar would
have said, &amp;ldquo;The fault, dear Brutus, is not in our stars, but in our software.&amp;rdquo;&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;So far I&amp;rsquo;ve only written a rather trivial MIDI player. The path to an SMF or
RIFF encoded MIDI file is provided as the command line argument, the software
decodes and at the appropriate time sends the MIDI event to the S2
daughterboard for synthesis. I also confirmed manual playback of files on
a floppy disk worked.&lt;/p&gt;
&lt;p&gt;Shortly thereafter, I switched from Raspbian to a stripped down Linux
distribution. Much to my horror, I found the floppy drive no longer worked. Any
attempt to mount or otherwise interact with the floppy drive would completely
freeze the running program, and no amount of SIGINT or SIGTERM would regain
control. The floppy drive had to be disconnected from the Pi.&lt;/p&gt;
&lt;p&gt;I spent the next several weeks debugging why the floppy drive stopped working.
I started with debugging the floppy drive itself, then confirmed the power
supply was outputting the correct voltages and was clean. I even recompiled the
Linux kernel with different options!&lt;/p&gt;
&lt;p&gt;Eventually, I discovered the drive worked with the 4.14 Linux kernel, but
stopped working some point thereafter. Facing an 8-month range of changes to
the kernel, I turned to git-bisect and &lt;a href=&#34;https://archive.vn/IqsHn&#34;&gt;started a week-long hunt&lt;/a&gt; for the
breaking change. Since this range was so large, and the underlying files so
different, setting up a compilation cache did not decrease the rebuild
durations.&lt;/p&gt;
&lt;p&gt;After at least 16 bisection points, I was lead to &lt;a href=&#34;https://github.com/torvalds/linux/commit/38d2b5fb75c15923fb89c32134516a623515bce4&#34;&gt;a single commit&lt;/a&gt;
in the kernel driver for the USB controller used by the Raspberry Pi, the
DesignWare USB2. In this change, a communication delay was added after multiple
successive NAKs in the USB protocol, to allow low-speed Chromebooks (and other
ARM-based devices) to do other work, instead of being tied up in an interrupt
loop.&lt;/p&gt;
&lt;p&gt;The commit requests a 1 millisecond delay, but uses the kernel&amp;rsquo;s low-resolution
timer API. On devices where this timer is coarse, like the Raspberry Pi, this
delay might actually be closer to 10-20 milliseconds. Fortunately, most USB
devices tolerate delays in the communication. Unfortunately, this drive is not
one that does so. This drive only tolerates delays of up to 5 milliseconds.&lt;/p&gt;
&lt;p&gt;With some help from some of the maintainers of the module, I was able to use
the kernel&amp;rsquo;s Ftrace  functionality to be able to debug without losing the use
of my serial TTY. I was then able to rapidly iterate with changes to the kernel
module, and load the rebuilt module into the running kernel using rmmod and
insmod.&lt;/p&gt;
&lt;p&gt;I have &lt;a href=&#34;https://patchwork.kernel.org/patch/10593569/&#34;&gt;submitted my first ever kernel patch&lt;/a&gt;&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt;, to convert the delay to the high
resolution API. This API has a finer timer, allowing the delay to operate much
closer to the requested 1 millisecond on retries.&lt;/p&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next Steps&lt;/h2&gt;
&lt;h3 id=&#34;complete-player-software&#34;&gt;Complete Player Software&lt;/h3&gt;
&lt;p&gt;With my kernel patch in hand, I can now return to work on the MIDI player
software. I want to be able to insert a floppy disk and, with one button, load
all the MIDI files stored there into a playlist and began playback. Then I will
add buttons for the other basic playback controls.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been happy with using &lt;a href=&#34;https://gokrazy.org/&#34;&gt;go-krazy&lt;/a&gt; as the Linux distribution. Writing the MIDI
player in Go has been pretty nice, and it makes the whole project feel fast.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m now battle testing the MIDI parser to make sure my player can withstand
whatever is thrown at it. I hope to make this available as open source software
by the time I publish the next part of this blog.&lt;/p&gt;
&lt;p&gt;Thinking farther into the future, the following project would add a display for
album and track metadata.&lt;/p&gt;
&lt;h3 id=&#34;custom-pcb-and-case&#34;&gt;Custom PCB and Case&lt;/h3&gt;
&lt;p&gt;I also aim to design a custom PCB for the synthesizer, buttons, display, and
RTC, as well as integration with a power supply that can signal the Pi to
shutdown before removing power.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t yet decided if I&amp;rsquo;ll do this PCB as a custom hat, or, since I&amp;rsquo;m not
using the majority of the hardware functions, as a board housing the Raspberry
Pi Compute Module. The decision here affects the case design.&lt;/p&gt;
&lt;p&gt;Like the software, I plan on releasing both the PCB and the case designs as&lt;/p&gt;
&lt;h3 id=&#34;midi-albums&#34;&gt;MIDI Albums&lt;/h3&gt;
&lt;p&gt;To really sell the illusion that, on an alternate timeline for the 90s, we
bought our pre-recorded music as MIDI on floppies, I&amp;rsquo;d love to commission
musicians to compose some awesome tracks that can be packaged onto a floppy.
Let me know via &lt;a href=&#34;mailto:terin@terinstock.com&#34;&gt;email&lt;/a&gt; if this appeals to you.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Except, perhaps, the floppy drive itself.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;There are no 12V rails in the current design, as the
WaveBlaster-compatible, which we&amp;rsquo;re able to talk about, doesn&amp;rsquo;t require it.
For compatibility, I&amp;rsquo;m wanting to include them in the final design.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:3&#34;&gt;
&lt;p&gt;If not Caesar, then perhaps Peter van Houten.&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:4&#34;&gt;
&lt;p&gt;At this time, it is still pending review for a future kernel release.&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </item>
    
    <item>
      <title>Black-box Serial Testing</title>
      <link>https://terinstock.com/post/2018/07/Black-box-Serial-Testing/</link>
      <pubDate>Fri, 27 Jul 2018 17:43:56 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2018/07/Black-box-Serial-Testing/</guid>
      <content type="html">&lt;p&gt;Occasionally, we all end up working on a program that could, at best, be
described as a ball of mud. We want to refactor it into smaller, more testable
components, but we don’t want to risk breaking it in the process.&lt;/p&gt;
&lt;p&gt;Enter the concept of “black-box testing”. In black-box testing, the
functionality of a program is tested by a tester, without the ability to
directly leverage implementation details of the program.&lt;/p&gt;
&lt;p&gt;For some programs, all we need to do is give it input, and observe the output.
As the software grows in complexity, we may have to compare created files,
setup mock web servers, and test databases. Many the great author and many the
great blog have written on these problems.&lt;/p&gt;
&lt;p&gt;The “ball of mud” side project I’m currently working on communicates over
a serial port. This presents a larger challenge for a tester: how can we
observe behavior of the application with the serial port from our test?
Pointing the program at a file won’t work, as it wants to connect to a remote
device and configure options, such as baud rate and parity bits.&lt;/p&gt;
&lt;p&gt;The long evolutionary history of UNIX provides us with one solution. In the
early days of UNIX, the output from the computer was printed onto serially
connected teleprinter. When technology marched on, terminal emulators replaced
the physical teleprinter, and the functionality needed to create these
pseudo-teleprinters was added to the kernel.&lt;/p&gt;
&lt;p&gt;As these teleprinters were originally serially connected, their modern virtual
forms retain their serial nature.&lt;/p&gt;
&lt;p&gt;In our test harness, we can create a new pseudo-teleprinter then pass the
emulated end to our program as the serial port. This example uses the Go test
harness, but equivalent functionality is available in most programming and
scripting languages, so you can use your favorite.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;package&lt;/span&gt; main
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;io/ioutil&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;os/exec&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;testing&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;github.com/google/go-cmp/cmp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;github.com/pkg/term/termios&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#ff0&#34;&gt;TestCLI&lt;/span&gt;(t *testing.T) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// create and open the pseudo tty master, and the linked
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// child tty.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	pty, tty, err := termios.&lt;span style=&#34;color:#ff0&#34;&gt;Pty&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		t.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;unable to create pty: %s\n&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// execute our program, passing in the child tty name (eg, /dev/pts/1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// as our serial port paramater.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	cmd := exec.&lt;span style=&#34;color:#ff0&#34;&gt;Command&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ball-of-mud&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;-serialport&amp;#34;&lt;/span&gt;, tty.&lt;span style=&#34;color:#ff0&#34;&gt;Name&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	output, err := cmd.&lt;span style=&#34;color:#ff0&#34;&gt;Output&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		t.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;error running ball-of-mud: %s\n&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// close the child tty. if we forget to do this the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// master pseudo tty will wait for more data forever.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	tty.&lt;span style=&#34;color:#ff0&#34;&gt;Close&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// read all the written serial data from the master
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// pseudo tty. ReadAll reads until EOF or an error is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// returned, but Linux sends EIO when attempting to read
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// from a master with no open children. So let&amp;#39;s just
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// look for the error string.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	bytes, err := ioutil.&lt;span style=&#34;color:#ff0&#34;&gt;ReadAll&lt;/span&gt;(pty)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; err != &lt;span style=&#34;color:#f00&#34;&gt;nil&lt;/span&gt; &amp;amp;&amp;amp; err.&lt;span style=&#34;color:#ff0&#34;&gt;Error&lt;/span&gt;() != &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;read ptm: input/output error&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		t.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;error reading pty: %s\n&amp;#34;&lt;/span&gt;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// confirm we saw the expected text from standard out
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; diff := cmd.&lt;span style=&#34;color:#ff0&#34;&gt;Diff&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;, string(output)); diff != &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		t.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;unexpected output (-want +got):\n%s&amp;#34;&lt;/span&gt;, diff)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#0f0&#34;&gt;// confirm we saw the expected bytes written to the serial device
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0f0&#34;&gt;&lt;/span&gt;	&lt;span style=&#34;color:#f00&#34;&gt;if&lt;/span&gt; diff := cmd.&lt;span style=&#34;color:#ff0&#34;&gt;Diff&lt;/span&gt;([]&lt;span style=&#34;color:#ee82ee&#34;&gt;byte&lt;/span&gt;{&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt;}, bytes); diff != &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		t.&lt;span style=&#34;color:#ff0&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;unexpected serial (-want +got):\n%s&amp;#34;&lt;/span&gt;, diff)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As the two parts of the pseudo-teleprinter are connected to each other, writes
to one will be readable from the other side. Thus duplex communication is
possible for more complex black-box tests.&lt;/p&gt;
&lt;p&gt;This method isn’t entirely perfect. For example, on Linux, it’s not always
possible to observe the configured baud rate. Even with these limitations, it’s
still a helpful tool in our toolkit.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Controller: Extending your K8s cluster</title>
      <link>https://terinstock.com/talk/2018/05/Controller-Extending-your-K8s-cluster/</link>
      <pubDate>Fri, 04 May 2018 14:45:00 +0200</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/talk/2018/05/Controller-Extending-your-K8s-cluster/</guid>
      <content type="html">&lt;p&gt;Building your own Kubernetes Controllers to extend the functionality of your cluster, presented at
&lt;a href=&#34;https://events.linuxfoundation.org/events/kubecon-cloudnativecon-europe-2018/&#34;&gt;KubeCon + CloudNativeCon Europe 2018&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;iframe id=&#34;talk_frame_442352&#34; class=&#34;speakerdeck-iframe&#34; src=&#34;//speakerdeck.com/player/413d79d1f9d64a5996ea23bf72367ebc&#34; width=&#34;710&#34; height=&#34;399&#34; style=&#34;aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;&#34; frameborder=&#34;0&#34; allowtransparency=&#34;true&#34; allowfullscreen=&#34;allowfullscreen&#34;&gt;&lt;/iframe&gt;


&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube-nocookie.com/embed/TM-2GgQ6Q2A&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Go Internalization and Localization</title>
      <link>https://terinstock.com/talk/2018/05/Go-Internalization-and-Localization/</link>
      <pubDate>Wed, 02 May 2018 19:00:00 +0200</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/talk/2018/05/Go-Internalization-and-Localization/</guid>
      <content type="html">&lt;p&gt;An introduction to Go&amp;rsquo;s text package, which allows developers to build localized applications and services.
Presented at &lt;a href=&#34;https://www.meetup.com/Go-Cph/&#34;&gt;Go Cph&lt;/a&gt; on May 2, 2018.&lt;/p&gt;
&lt;iframe id=&#34;talk_frame_442170&#34; class=&#34;speakerdeck-iframe&#34; src=&#34;//speakerdeck.com/player/cf7b122a2a504206ab1ad5b45d199c2f&#34; width=&#34;710&#34; height=&#34;399&#34; style=&#34;aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;&#34; frameborder=&#34;0&#34; allowtransparency=&#34;true&#34; allowfullscreen=&#34;allowfullscreen&#34;&gt;&lt;/iframe&gt;

</content>
    </item>
    
    <item>
      <title>Extending Kubernetes</title>
      <link>https://terinstock.com/talk/2018/05/Extending-Kubernetes/</link>
      <pubDate>Tue, 01 May 2018 18:50:00 +0200</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/talk/2018/05/Extending-Kubernetes/</guid>
      <content type="html">&lt;p&gt;Talk on extending Kubernetes Dyanmic Admission control with webhooks, presented at &lt;a href=&#34;https://www.meetup.com/GOTO-Nights-CPH/events/249895973/&#34;&gt;GOTO Cph&lt;/a&gt;
in Copenhagen. This talk was given as being related to our talk at &lt;a href=&#34;https://events.linuxfoundation.org/events/kubecon-cloudnativecon-europe-2018/&#34;&gt;KubeCon + CloudNativeCon Europe&lt;/a&gt;.&lt;/p&gt;
&lt;iframe id=&#34;talk_frame_442031&#34; class=&#34;speakerdeck-iframe&#34; src=&#34;//speakerdeck.com/player/988fcc7d4f3944dbb6eb96b407bd271d&#34; width=&#34;710&#34; height=&#34;399&#34; style=&#34;aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;&#34; frameborder=&#34;0&#34; allowtransparency=&#34;true&#34; allowfullscreen=&#34;allowfullscreen&#34;&gt;&lt;/iframe&gt;

</content>
    </item>
    
    <item>
      <title>Game Boy Collage #1</title>
      <link>https://terinstock.com/post/2017/11/game-boy-collage-1/</link>
      <pubDate>Mon, 13 Nov 2017 05:34:31 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2017/11/game-boy-collage-1/</guid>
      <content type="html">&lt;p&gt;For a few months now, I&amp;rsquo;ve wanted to work on some new old-school artwork: creating a collage from
the internals of a classic Nintendo Game Boy. Similar to the How Things Work series of books, each
component of the Game Boy would have annotations and detailed schematics. Unlike those books, the
Game Boy would continue to work. After all, if it doesn&amp;rsquo;t work, it&amp;rsquo;s not very fun.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/trio.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;8-bit handhelds&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Although I&amp;rsquo;ll only be taking apart one, I have three 8-bit handhelds.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-front-smurfs.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Game Boy (DMG) playing Smurfs&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;On the left is the original gray Game Boy. It sports a super reflective black and green screen,
guzzling 4 AA batteries, and a big boxy shape.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/gbc-front-mariotennis.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Game Boy Color (GBC) playing Mario Tennis&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;To the right in the center is a lime green Game Boy Color, capable of up to 32K colors, and backward
compatible with the original Game Boy. I have fond memories playing on my lime green Game Boy Color
growing up. I can&amp;rsquo;t even guess how many hours I put on it.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/hfgbc-front-mariotennis.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;GB Boy Colour playing Mario Tennis&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Finally, on the rightmost side, is the GB Boy Colour, a 2010s clone of the Color. The screen is the
wrong aspect ratio and the wrong resolution. The custom processor runs the games at the wrong clock
speed. The audio hardware, while loud, is a poor emulation of the original. It does, and this is the
saving grace, have a backlight LCD screen.&lt;/p&gt;
&lt;p&gt;My understanding is that this uses a custom System on Chip design to emulate the Game Boy Color.
I haven&amp;rsquo;t taken it apart, but I think it will be interesting to do so in a future post.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-back.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Backside of DMG. Serial Number G27198639&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-back-battery.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Backside of DMG with the battery compartment visible&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Moving back to the original, gray, Game Boy unit, before we take it apart, I should take some
pictures of the outside. The serial number and service stickers aren&amp;rsquo;t in pristine condition, but
they&amp;rsquo;re still in pretty decent shape! I&amp;rsquo;d like to repair both labels; I&amp;rsquo;m looking into the best
repair methods.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-side-left.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Left side of the DMG&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-side-right.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Right side of the DMG&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-side-bottom.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Bottom side of the DMG&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The sides look great, too. I&amp;rsquo;m surprised that the condition is this good. It includes a 3.5mm
headphone jack, as electronics should.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-lcd-back.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Backside of the LCD board in the case&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-mb-back.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Backside of the main board in the case&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;After popping it open, I find that it&amp;rsquo;s relatively dust free. The Game Boy separates into two main
pieces, connected by a ribbon cable. The top board contains the LCD screen, input controls, and
contrast knob. The bottom board houses the main CPU, coprocessors, audio amplifiers, volume knob,
and cartridge loader. Two daughterboards are attached to the bottom board, one for power regulation,
the other for the 3.5mm jack.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-mb-full.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Front of the main board, along with the power and audio jack boards&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The manufacturing date of both boards is July 1992, based on the date codes stamped. That means
these capacitors are at least 25 years old. Since most manufacturers rate their capacitors for 15-20
years, I should replace them before this project gets to far along.  The boards are otherwise in
great shape.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-front-case-inside.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Inside of the front case&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Unfortunately, on closer inspection, this unit has two broken standoffs on the front case. I don&amp;rsquo;t
think it&amp;rsquo;s a problem: you won&amp;rsquo;t be able to see them in the final design.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-battery-contacts.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;DMG&amp;#39;s battery contacts are covered in acid&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-battery-compartment-acid.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;Battery acid left behind in DMG&amp;#39;s battery compartment&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The biggest problem is the corrosion in the battery compartment. All the terminals were pretty
corroded. They were pretty easy to pop out, but lots of battery acid stayed behind in the case.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-lime-juice-solution.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;A quick aside to the kitchen. For Science!&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Silly me, I had no terminal cleaner available, nor any other acidic household cleaner. So
I improvised a bit: I had an extra lime in the kitchen from a previous meal. Lime juice is a citric
acid with a pH about the same as lemon juice and vinegar, so I figured it would work just as well.
It&amp;rsquo;s also fitting, because, you know, both color handhelds are lime green.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-battery-contacts-clean.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;No more acid remains&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;&lt;img src=&#34;https://terinstock.com/media/dmg/dmg-battery-compartment-clean.jpg&#34;/&gt;&lt;figcaption&gt;
            &lt;h4&gt;No more acid remains&lt;/h4&gt;
        &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;After cleaning, it appears the battery acid etched some of the shininess away. But it otherwise
cleaned up nicely. I cleaned up the battery acid in the case using a cotton swab along with the lime
juice.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it for now. In the next installment, I will share the plan for how breaking up the Game Boy
and wiring it together. I also have ideas for future enhancements I think would be cool. I&amp;rsquo;ve also
ordered a set of replacement capacitors; I&amp;rsquo;ll do a post on replacing them once they arrive.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Aliasing JavaScript Modules</title>
      <link>https://terinstock.com/post/2017/06/Aliasing-JavaScript-Modules/</link>
      <pubDate>Tue, 20 Jun 2017 17:45:55 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2017/06/Aliasing-JavaScript-Modules/</guid>
      <content type="html">&lt;p&gt;A number of years ago I wrote a &lt;a href=&#34;https://terinstock.com/post/2014/02/Replacing-packages-in-a-Browserify-bundle/#the-solution&#34;&gt;blog post&lt;/a&gt; detailing how to replace modules in a
Browserify bundle. In the interceding years, the JavaScript ecosystem has had at
least three lifetimes, and we&amp;rsquo;re now due for an update.&lt;/p&gt;
&lt;h2 id=&#34;browserify&#34;&gt;Browserify&lt;/h2&gt;
&lt;p&gt;As the last post focused entirely on Browserify, I&amp;rsquo;ll first revisit it first. As
noted in the previous post, the &lt;code&gt;browserify-swap&lt;/code&gt; transform is designed to swap
individual files, not modules. We were able to work around it by defining the
configuration as a RegExp, but it&amp;rsquo;s easy to get wrong. You can now use the
&lt;a href=&#34;https://github.com/benbria/aliasify&#34;&gt;aliasify&lt;/a&gt; transform, which does support replacing modules.&lt;/p&gt;
&lt;p&gt;After installing, enable the transform, and add a new section to your
package.json.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;browserify&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#34;transform&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;aliasify&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;aliasify&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#34;aliases&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#34;underscore&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;lodash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In addition to aliasing modules, aliasify supports replacments that are relative
to the file calling require, as well as using RegExps to define replacement.&lt;/p&gt;
&lt;h2 id=&#34;webpack&#34;&gt;Webpack&lt;/h2&gt;
&lt;p&gt;Webpack supports aliasing modules &lt;a href=&#34;https://webpack.js.org/configuration/resolve/#resolve-alias&#34;&gt;right out of the box&lt;/a&gt;, no plugins needed. Just
add an additional section to your Webpack configuration.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;resolve&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#34;alias&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#34;underscore&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;lodash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;babel&#34;&gt;Babel&lt;/h2&gt;
&lt;p&gt;You can also setup aliases in Babel with the &lt;a href=&#34;https://github.com/tleunen/babel-plugin-module-resolver&#34;&gt;&lt;code&gt;babel-plugin-module-resolver&lt;/code&gt;&lt;/a&gt; plugin. Like
the above, just add a section to your Babel configuration.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;plugins&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        [&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;module-resolver&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#34;root&amp;#34;: [&lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#34;alias&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &amp;#34;underscore&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;lodash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;yarn&#34;&gt;Yarn&lt;/h2&gt;
&lt;p&gt;Finally, you can alias before dependencies are even written to disk if you use
Yarn&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. Since it breaks compatibility with npm, it&amp;rsquo;s best if you only use this
method in private applications, not public applications or in libraries.&lt;/p&gt;
&lt;p&gt;In this method you combine the alias with defining your dependencies in
package.json.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;dependencies&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#34;underscore&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;npm:lodash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unlike the other methods in this guide, using the Yarn method also allows you to
install the same dependency at different versions by giving them different
names.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;dependencies&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#34;lodash3&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;npm:lodash@^3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;#34;lodash4&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;npm:lodash@^4&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Unfortunately, it seems this isn&amp;rsquo;t yet in the documentation. It has, however, been a &lt;a href=&#34;https://archive.vn/scQRm&#34;&gt;&amp;ldquo;Yarn tip&amp;rdquo;&lt;/a&gt;.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </item>
    
    <item>
      <title>A rose by any other name…</title>
      <link>https://terinstock.com/talk/2017/03/A-rose-by-any-other-name/</link>
      <pubDate>Wed, 15 Mar 2017 06:10:00 -0700</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/talk/2017/03/A-rose-by-any-other-name/</guid>
      <content type="html">&lt;p&gt;I gave the following Browserify talk at the Webpack edition of NodeSource&amp;rsquo;s
monthly meetup. Webpack&amp;rsquo;s Sean Larkin had given his &amp;ldquo;&lt;a href=&#34;https://www.youtube.com/watch?v=AZPYL30ozCY&#34;&gt;Core Concepts&lt;/a&gt;&amp;rdquo; talk before
I went on, and I made a comparison to Browserify&amp;rsquo;s concepts.&lt;/p&gt;
&lt;iframe id=&#34;talk_frame_383901&#34; class=&#34;speakerdeck-iframe&#34; src=&#34;//speakerdeck.com/player/1d2a0cb9884b494ea0513561846cb8b7&#34; width=&#34;710&#34; height=&#34;399&#34; style=&#34;aspect-ratio:710/399; border:0; padding:0; margin:0; background:transparent;&#34; frameborder=&#34;0&#34; allowtransparency=&#34;true&#34; allowfullscreen=&#34;allowfullscreen&#34;&gt;&lt;/iframe&gt;

&lt;p&gt;While my talk is chiefly satirical, I get serious towards the end: Webpack is
powerful because of how configurable it is. On the other hand, people like
Browserify because it&amp;rsquo;s opinionated, so you can focus on building your project.
I hope to see improvements to Webpack in 2017 that make it more accessible.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>TLS with Erlang</title>
      <link>https://terinstock.com/post/2014/07/TLS-with-Erlang/</link>
      <pubDate>Wed, 02 Jul 2014 00:00:00 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2014/07/TLS-with-Erlang/</guid>
      <content type="html">&lt;p&gt;I recently configured an HTTP server written in Erlang for secure communication with Transport Layer Security (TLS), successor to Secure Sockets Layer (SSL). Unfortunately, my attempt resulted in TLS errors from both browsers and command line tools.  Determined to find a solution, I dug into the HTTP server and Erlang&amp;rsquo;s SSL library to resolve these TLS connection failures.&lt;/p&gt;
&lt;p&gt;In the process I uncovered issues with intermediate bundles and elliptic curve selections, as well as a configuration optimization.&lt;/p&gt;
&lt;h2 id=&#34;bundling-intermediate-certificates&#34;&gt;Bundling Intermediate Certificates&lt;/h2&gt;
&lt;p&gt;When you buy a certificate for your site, you&amp;rsquo;re likely to also receive an intermediate bundle. This intermediate bundle is a chain of certificates that bind the certificate issued for your site to a trusted certificate located on the user&amp;rsquo;s computer or browser (a &amp;ldquo;root certificate&amp;rdquo;). If this bundle is excluded, the browser won&amp;rsquo;t trust the connection, since it can&amp;rsquo;t verify each link of the chain.&lt;/p&gt;
&lt;p&gt;The issuer reminds you to add this bundle to your server&amp;rsquo;s configuration—it is important not to forget this step! Erlang&amp;rsquo;s SSL library can be configured to include the intermediate bundle by providing the path to the &lt;code&gt;cacertfile&lt;/code&gt; option.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-erlang&#34; data-lang=&#34;erlang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssl:&lt;span style=&#34;color:#ff0&#34;&gt;start&lt;/span&gt;().
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ok, &lt;span style=&#34;color:#eedd82&#34;&gt;ListenSocket&lt;/span&gt;} = ssl:&lt;span style=&#34;color:#ff0&#34;&gt;listen&lt;/span&gt;(&lt;span style=&#34;color:#f60&#34;&gt;443&lt;/span&gt;, [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {certfile, &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/full/path/to/server_cert.pem&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {keyfile, &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/full/path/to/server_key.pem&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {cacertfile, &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;/full/path/to/bundle.pem&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssl:&lt;span style=&#34;color:#ff0&#34;&gt;transport_accept&lt;/span&gt;(&lt;span style=&#34;color:#eedd82&#34;&gt;ListenSocket&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Erlang will now send the entire certificate chain to the browser during the connection, and the browser can trust the connection.&lt;/p&gt;
&lt;h2 id=&#34;ode-to-debugging-tls&#34;&gt;Ode to Debugging TLS&lt;/h2&gt;
&lt;p&gt;At this point, I expected to be done. Unfortunately, while OpenSSL and TLS scanning tools such as &lt;a href=&#34;https://github.com/iSECPartners/sslyze&#34;&gt;sslyze&lt;/a&gt; would connect fine, my copies of Chrome, Firefox, and curl refused to connect with cryptic SSL errors such as &lt;code&gt;ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED&lt;/code&gt; and &lt;code&gt;sec_error_invalid_key&lt;/code&gt;. Chrome&amp;rsquo;s debugging logs provided no additional information.&lt;/p&gt;
&lt;h3 id=&#34;tls-handshakes-a-primer&#34;&gt;TLS Handshakes: A Primer&lt;/h3&gt;
&lt;p&gt;To start a connection to a secure site, the client and browser must first configure the secure connection in a process commonly called a &amp;ldquo;TLS Handshake&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In a full handshake, two roundtrips between the browser and the server are required:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The browser sends a &lt;code&gt;ClientHello&lt;/code&gt; message to the server.&lt;/p&gt;
&lt;p&gt;This message includes the highest TLS version supported, lists of supported cipher suites and compression algorithms, and other TLS extensions, including a list of known named elliptic curves.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The server responds with &lt;code&gt;ServerHello&lt;/code&gt;, &lt;code&gt;Certificate&lt;/code&gt;, an optional &lt;code&gt;ServerKeyExchange&lt;/code&gt; message, and &lt;code&gt;ServerHelloDone&lt;/code&gt; messages.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ServerHello&lt;/code&gt; message details specific options used for the connections including the TLS version, the cipher suite, the compression algorithm, and any additional TLS extensions. The TLS version should be the highest version support by both client and server. The server maintains a priority list for cipher suites and compression algorithms, and it selects the highest priority supported by the browser.&lt;/p&gt;
&lt;p&gt;The server then attaches the entire certificate chain in a &lt;code&gt;Certificate&lt;/code&gt; message, so the browser can verify the authenticity of the connection.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;Certificate&lt;/code&gt; message doesn&amp;rsquo;t contain enough information to allow a client to exchange a session key, such as with a Diffie–Hellman key exchange, then a &lt;code&gt;ServerKeyExchange&lt;/code&gt; message is included.&lt;/p&gt;
&lt;p&gt;The server then ends with &lt;code&gt;ServerHelloDone&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The client responds with a &lt;code&gt;ClientKeyExchange&lt;/code&gt;, &lt;code&gt;ChangeCipherSpec&lt;/code&gt; and &lt;code&gt;Finished&lt;/code&gt; messages.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ClientKeyExchange&lt;/code&gt; message contains a secret key determined by both sides using public key encryption. The specifics of how the session key is generated is outside the scope of this primer.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;ChangeCipherSpec&lt;/code&gt; message, the browser tells the server to switch to encrypted communication for the rest of the communication.&lt;/p&gt;
&lt;p&gt;To verify the integrity of the communications up to this point, a hash of the previous messages is taken and sent as part of the &lt;code&gt;Finished&lt;/code&gt; message. The server will also compute a hash over the same messages and compare.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, the server sends &lt;code&gt;ChangeCipherSpec&lt;/code&gt; and &lt;code&gt;Finished&lt;/code&gt; messages.&lt;/p&gt;
&lt;p&gt;The server verifies the hash sent in the client&amp;rsquo;s &lt;code&gt;Finished&lt;/code&gt; message, then acknowledges the encryption communications and finishes, sending a similar hash to the client.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;the-failed-tls-handshake&#34;&gt;The Failed TLS Handshake&lt;/h3&gt;
&lt;p&gt;Interested in understanding the exact exchange between my browser and the server, I logged the TLS traffic with the network protocol analyzer Wireshark. The browser and the server successfully exchanged &lt;code&gt;ClientHello&lt;/code&gt;, &lt;code&gt;ServerHello&lt;/code&gt;, &lt;code&gt;Certificate&lt;/code&gt;, &lt;code&gt;ServerKeyExchange&lt;/code&gt;, and &lt;code&gt;ServerHelloDone&lt;/code&gt; messages before the browser unexpectedly closed the connection.&lt;/p&gt;
&lt;p&gt;Inspecting the &lt;code&gt;ServerHello&lt;/code&gt; message informed me the server agreed on using TLS 1.2 and choose the &lt;code&gt;TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA&lt;/code&gt; cipher suite. Both supported by the browser.&lt;/p&gt;
&lt;p&gt;From &lt;a href=&#34;http://tools.ietf.org/rfc/rfc4492.txt&#34;&gt;RFC4492&lt;/a&gt;, when the &lt;code&gt;ECDHE_ECDSA&lt;/code&gt;, &lt;code&gt;ECDHE_RSA&lt;/code&gt;, or &lt;code&gt;ECDH_anon&lt;/code&gt; ciphers are chosen, a &lt;code&gt;ServerKeyExchange&lt;/code&gt; message is sent containing the ECDHE public key used to derive the shared key.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ServerKeyExchange&lt;/code&gt; portion of the TLS handshake from the server to the client is replicated below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0000   0c 00 01 49 03 00 16 41 04 b2 33 23 71 c9 da 80
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0010   94 d3 ec eb 05 9f e5 36 91 a7 e2 e5 40 78 aa 03
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0020   38 4f eb 7c 36 1b 92 21 58 cf c3 e5 b7 08 40 5a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0030   6a eb d2 6a 22 90 e0 47 28 ce 70 9b bb 87 17 d3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0040   4a bc 7c 78 14 ef 97 0d 0d 02 01 01 00 91 7e 3c
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0050   ce 9f 06 1d 00 47 4f 53 85 df 2e 04 31 9a 14 a3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0060   25 bd 51 b3 1a 0f dd 3b c3 f4 25 b0 23 d5 34 0a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0070   a3 fc 2a e2 08 34 29 87 00 91 0e 10 6a 40 b3 b5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0080   61 0c 77 9a 8a 0c 50 dc 78 57 ab 2a 51 66 d0 0d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0090   b7 3c 4d c0 28 b9 06 b4 f5 f6 48 f6 5a 02 c2 7e
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00a0   8f b2 ac 4b 03 3a 40 c0 e2 c6 2f 77 61 58 ea 0d
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00b0   ab 6c 7f 57 be e1 03 0b c6 1e 2a b0 67 ab c2 db
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00c0   0a 5b c4 ab 51 9a 76 e6 75 2d e6 ca ce 06 4b f5
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00d0   8f dc f0 c1 42 65 14 c0 79 80 51 f2 68 3b 4a 51
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00e0   0d 50 5a 01 32 e3 5c 8d cd 8c ec c1 c4 fa 84 3a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;00f0   33 37 4c 9d d5 54 f9 6c aa b8 27 27 7b 4a 7c 33
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0100   27 8e 48 48 33 87 73 11 9b 92 0b e3 99 49 23 7b
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0110   c5 ab 53 ef f2 86 df 56 e5 97 6b 2d 93 5f c0 8a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0120   e6 68 4f 6b 3a 1b 55 26 08 aa c0 36 74 21 ed cc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0130   0e c9 22 0b 97 51 c1 01 48 3f 01 d2 74 fe 36 18
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;0140   5f 5c 91 47 b3 19 1c 00 69 7f 17 1b c3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0c&lt;/code&gt; indicates this message is a &lt;code&gt;ServerKeyExchange&lt;/code&gt; message&lt;/li&gt;
&lt;li&gt;&lt;code&gt;00 01 49&lt;/code&gt; is the length of 0x000149 (in decimal, 329) bytes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;03&lt;/code&gt; is the elliptic curve type, in this case &amp;ldquo;named_curve&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;00 16&lt;/code&gt; is the named curve, &lt;code&gt;secp256k1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The ECDHE public key and signature follows.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point the browser verifies the signature and retrieves the elliptic curve parameters and ECDHE public key from the &lt;code&gt;ServerKeyExchange&lt;/code&gt; message.&lt;/p&gt;
&lt;p&gt;Section 5.4 of RFC4492 ends with the following note:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A possible reason for a fatal handshake failure is that the client&amp;rsquo;s capabilities for handling elliptic curves and point formats are exceeded&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While Erlang supports all 25 elliptic curves named in RFC4492, common browsers only support a smaller subset of two or three.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; In the above snippet, we see that Erlang chose &lt;code&gt;secp256k1&lt;/code&gt;, the elliptic curve used in &lt;a href=&#34;https://en.bitcoin.it/wiki/Secp256k1&#34;&gt;Bitcoin&lt;/a&gt;, and not one supported by my browsers.&lt;/p&gt;
&lt;p&gt;Erlang&amp;rsquo;s early support of elliptic curves are problematic. When picking an elliptic curve, Erlang does not consider the list of supported curves sent by the browser. This has been resolved with the &lt;a href=&#34;http://www.erlang.org/download_release/23&#34;&gt;Erlang R16R03-1&lt;/a&gt; release.&lt;/p&gt;
&lt;h2 id=&#34;configuration-of-tls-and-ciphers&#34;&gt;Configuration of TLS and Ciphers&lt;/h2&gt;
&lt;p&gt;Erlang&amp;rsquo;s SSL library has defaults for the TLS versions, cipher suites and renegotiation behavior. You may want to change these options for client compatibility and for resiliency to TLS attacks.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-erlang&#34; data-lang=&#34;erlang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssl:&lt;span style=&#34;color:#ff0&#34;&gt;start&lt;/span&gt;().
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ok, &lt;span style=&#34;color:#eedd82&#34;&gt;ListenSocket&lt;/span&gt;} = ssl:&lt;span style=&#34;color:#ff0&#34;&gt;listen&lt;/span&gt;(&lt;span style=&#34;color:#f60&#34;&gt;443&lt;/span&gt;, [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {server_renegotiate, true},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {versions: [ &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;tlsv1.1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;tlsv1.2&amp;#34;&lt;/span&gt; ]},
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {ciphers: [ &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-ECDSA-AES128-SHA256&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;ECDHE-ECDSA-AES128-SHA&amp;#34;&lt;/span&gt; ]}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ]).
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssl:&lt;span style=&#34;color:#ff0&#34;&gt;transport_accept&lt;/span&gt;(&lt;span style=&#34;color:#eedd82&#34;&gt;ListenSocket&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://support.cloudflare.com/hc/en-us/articles/200933580&#34;&gt;CloudFlare publishes&lt;/a&gt; the cipher suites they use with nginx. You can check the ciphers supported by your Erlang installation by running the following in a &lt;code&gt;erl&lt;/code&gt; session.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-erlang&#34; data-lang=&#34;erlang&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff0&#34;&gt;rp&lt;/span&gt;(ssl:&lt;span style=&#34;color:#ff0&#34;&gt;cipher_suites&lt;/span&gt;(openssl)).
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I created &lt;a href=&#34;https://git-wip-us.apache.org/repos/asf?p=couchdb.git;a=commit;h=fdb2188&#34;&gt;a patch&lt;/a&gt; for CouchDB that adds the configuration options &lt;code&gt;secure_renegotiate&lt;/code&gt;, &lt;code&gt;ciphers&lt;/code&gt;, and &lt;code&gt;tls_versions&lt;/code&gt; to the SSL section:&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f00&#34;&gt;[ssl]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;certfile           = &lt;span style=&#34;color:#87ceeb&#34;&gt;/full/path/to/server_cert.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;keyfile            = &lt;span style=&#34;color:#87ceeb&#34;&gt;/full/path/to/server_key.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cacertfile         = &lt;span style=&#34;color:#87ceeb&#34;&gt;/full/path/tp/bundle.pem&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;secure_renegotiate = &lt;span style=&#34;color:#87ceeb&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tls_versions       = &lt;span style=&#34;color:#87ceeb&#34;&gt;[ &amp;#34;tlsv1.1&amp;#34;, &amp;#34;tlsv1.2&amp;#34; ]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ciphers            = &lt;span style=&#34;color:#87ceeb&#34;&gt;[ &amp;#34;ECDHE-ECDSA-AES128-SHA256&amp;#34;, &amp;#34;ECDHE-ECDSA-AES128-SHA&amp;#34; ]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;While presented through the lens of an HTTP server in Erlang, the same basic steps could be extrapolated to any secure server written in any language. Recapping my debugging process: First, I ensured the server is configured to send the entire certificate chain to the client. Then, I tested the connection with a TLS scanner or a network protocol analyzer. Finally, once the server is properly communicating, I took a look at my server&amp;rsquo;s TLS configuration to ensure it is secure and reflect current best practices.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; An earlier version of this post claimed browsers only supported three elliptic curves: &lt;code&gt;secp192r1&lt;/code&gt;, &lt;code&gt;secp224r1&lt;/code&gt;, and &lt;code&gt;secp256r1&lt;/code&gt;. This information was incorrectly included from an earlier draft.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; My patch did not land in CouchDB 1.6.0. CouchDB has since been reorganized into multiple repos, but my patch continues to be in place for a future stable release.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content>
    </item>
    
    <item>
      <title>Replacing packages in a Browserify bundle</title>
      <link>https://terinstock.com/post/2014/02/Replacing-packages-in-a-Browserify-bundle/</link>
      <pubDate>Thu, 27 Feb 2014 00:00:00 +0000</pubDate>
      <author>terin@terinstock.com (Terin Stock)</author>
      <guid>https://terinstock.com/post/2014/02/Replacing-packages-in-a-Browserify-bundle/</guid>
      <content type="html">&lt;p&gt;As a developer on a large Backbone application built with &lt;a href=&#34;https://github.com/substack/node-browserify&#34;&gt;Browserify&lt;/a&gt;, there are a number of occasions where I want to replace one dependency with another. In this specific case, I wanted to swap &lt;code&gt;underscore&lt;/code&gt; for &lt;code&gt;lodash&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Browserify already supports this with the &amp;ldquo;browser field&amp;rdquo; in &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is a special &amp;ldquo;browser&amp;rdquo; field you can set in your package.json on a per-module basis to override file resolution for browser-specific versions of files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This only works for resolution within your package, if any of your dependency packages require Underscore they&amp;rsquo;ll get Underscore. This is to help ensure your replacements don&amp;rsquo;t break your dependencies.&lt;/p&gt;
&lt;p&gt;However, it&amp;rsquo;s suboptimal to ship both Lo-Dash and Underscore, as is maintaining a fork simply to replace the dependency. In these edge cases, it&amp;rsquo;s useful to replace files or packages even within dependencies.&lt;/p&gt;
&lt;p&gt;Luckily, the Browserify transform &lt;a href=&#34;https://github.com/thlorenz/browserify-swap&#34;&gt;browserify-swap&lt;/a&gt; allows you swap dependencies in certain packages, as defined via the &lt;code&gt;@packages&lt;/code&gt; key, while generating the output bundle.&lt;/p&gt;
&lt;p&gt;As I want to replace Underscore in Backbone, Marionnette and related packages, the configuration seemed pretty straight-forward.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/* package.json */
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;browserify&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;transform&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;browserify-swap&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;browserify-swap&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;@packages&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;backbone&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;marionette&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;backbone.babysitter&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;backbone.wreqr&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;all&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;underscore.js$&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;lodash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I was a bit discouraged to find that Underscore was still present in the output bundle. After triple-checking that my configuration was valid, I broke out the node debugger to find what was wrong.&lt;/p&gt;
&lt;p&gt;I believed &lt;code&gt;browserify-swap&lt;/code&gt; to swap files while resolving the require calls. The transform actually checks if the current file path &lt;a href=&#34;https://github.com/thlorenz/browserify-swap/blob/fbb9ca86c8af14e3fa21a75852f6251ea86f45d7/index.js#L38&#34;&gt;matches a RegEx&lt;/a&gt; defined in the &lt;code&gt;package.json&lt;/code&gt; file and replaces the contents to require the swapped in file.&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The Solution&lt;/h2&gt;
&lt;p&gt;With this information in hand, it became clear that we needed to swap in the &lt;code&gt;underscore&lt;/code&gt; package.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/* package.json */
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;browserify&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;transform&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;browserify-swap&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;#34;browserify-swap&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;@packages&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;underscore&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;all&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;#34;underscore.js$&amp;#34;: &lt;span style=&#34;color:#87ceeb&#34;&gt;&amp;#34;lodash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This causes the swap to happen for each instance of Underscore in the bundle, but only the one instance of Lo-Dash would be included.&lt;/p&gt;
&lt;p&gt;I believe that the transform should also allow swapping packages for other packages, as the folder structure of a package is usually not part of the public API. I&amp;rsquo;ve &lt;a href=&#34;https://github.com/thlorenz/browserify-swap/issues/1&#34;&gt;opened an issue&lt;/a&gt; against the project.&lt;/p&gt;
</content>
    </item>
    
  </channel>
</rss>
